@atlaskit/editor-plugin-type-ahead 1.12.2 → 1.13.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,26 @@
1
1
  # @atlaskit/editor-plugin-type-ahead
2
2
 
3
+ ## 1.13.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#112096](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/112096)
8
+ [`5d95afdd358ac`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/5d95afdd358ac) -
9
+ [ux] Creates a package for new QuickInsert and Right Rail UI and adds it under a FF
10
+ - Updated dependencies
11
+
12
+ ## 1.13.0
13
+
14
+ ### Minor Changes
15
+
16
+ - [#105322](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/105322)
17
+ [`8876083532adc`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/8876083532adc) -
18
+ Bumped editor-prosemirror version to 7.0.0
19
+
20
+ ### Patch Changes
21
+
22
+ - Updated dependencies
23
+
3
24
  ## 1.12.2
4
25
 
5
26
  ### Patch Changes
@@ -7,8 +7,12 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.ContentComponent = ContentComponent;
8
8
  var _react = _interopRequireDefault(require("react"));
9
9
  var _hooks = require("@atlaskit/editor-common/hooks");
10
- var _TypeAheadMenu = require("./TypeAheadMenu");
10
+ var _typeAhead = require("@atlaskit/editor-common/type-ahead");
11
+ var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
12
+ var _TypeAheadMenu = require("./modern/TypeAheadMenu");
13
+ var _TypeAheadMenu2 = require("./TypeAheadMenu");
11
14
  function ContentComponent(_ref) {
15
+ var _typeAheadState$trigg;
12
16
  var api = _ref.api,
13
17
  editorView = _ref.editorView,
14
18
  popupMountRef = _ref.popupMountRef;
@@ -17,10 +21,21 @@ function ContentComponent(_ref) {
17
21
  if (!typeAheadState) {
18
22
  return null;
19
23
  }
20
- return /*#__PURE__*/_react.default.createElement(_TypeAheadMenu.TypeAheadMenu, {
21
- editorView: editorView,
22
- popupMountRef: popupMountRef,
23
- typeAheadState: typeAheadState,
24
- api: api
25
- });
24
+ if (
25
+ // TODO: Also requires a check for editor appearance (needs to be enabled for full-page/full-width editor only ?)
26
+ !(0, _experiments.editorExperiment)('platform_editor_controls', 'control') && ((_typeAheadState$trigg = typeAheadState.triggerHandler) === null || _typeAheadState$trigg === void 0 ? void 0 : _typeAheadState$trigg.id) === _typeAhead.TypeAheadAvailableNodes.QUICK_INSERT) {
27
+ return /*#__PURE__*/_react.default.createElement(_TypeAheadMenu.TypeAheadMenu, {
28
+ editorView: editorView,
29
+ popupMountRef: popupMountRef,
30
+ typeAheadState: typeAheadState,
31
+ api: api
32
+ });
33
+ } else {
34
+ return /*#__PURE__*/_react.default.createElement(_TypeAheadMenu2.TypeAheadMenu, {
35
+ editorView: editorView,
36
+ popupMountRef: popupMountRef,
37
+ typeAheadState: typeAheadState,
38
+ api: api
39
+ });
40
+ }
26
41
  }
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.TypeAheadMenu = void 0;
8
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
+ var _react = _interopRequireDefault(require("react"));
10
+ var _typeAhead = require("@atlaskit/editor-common/type-ahead");
11
+ var _useItemInsert3 = require("../hooks/use-item-insert");
12
+ var _TypeAheadPopup = require("./TypeAheadPopup");
13
+ var TypeAheadMenu = exports.TypeAheadMenu = /*#__PURE__*/_react.default.memo(function (_ref) {
14
+ var _popupMountRef$curren, _popupMountRef$curren2, _popupMountRef$curren3;
15
+ var editorView = _ref.editorView,
16
+ popupMountRef = _ref.popupMountRef,
17
+ typeAheadState = _ref.typeAheadState,
18
+ api = _ref.api;
19
+ var isOpen = typeAheadState.decorationSet.find().length > 0;
20
+ var triggerHandler = typeAheadState.triggerHandler,
21
+ items = typeAheadState.items,
22
+ errorInfo = typeAheadState.errorInfo,
23
+ decorationElement = typeAheadState.decorationElement,
24
+ decorationSet = typeAheadState.decorationSet,
25
+ query = typeAheadState.query;
26
+ var _useItemInsert = (0, _useItemInsert3.useItemInsert)(
27
+ // Ignored via go/ees005
28
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
29
+ triggerHandler, editorView, items, api),
30
+ _useItemInsert2 = (0, _slicedToArray2.default)(_useItemInsert, 2),
31
+ onItemInsert = _useItemInsert2[0],
32
+ onTextInsert = _useItemInsert2[1];
33
+ var insertItem = _react.default.useCallback(function () {
34
+ var mode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _typeAhead.SelectItemMode.SELECTED;
35
+ var index = arguments.length > 1 ? arguments[1] : undefined;
36
+ queueMicrotask(function () {
37
+ onItemInsert({
38
+ mode: mode,
39
+ index: index,
40
+ query: query
41
+ });
42
+ });
43
+ }, [onItemInsert, query]);
44
+ var cancel = _react.default.useCallback(function (_ref2) {
45
+ var setSelectionAt = _ref2.setSelectionAt,
46
+ addPrefixTrigger = _ref2.addPrefixTrigger,
47
+ forceFocusOnEditor = _ref2.forceFocusOnEditor;
48
+ var fullQuery = addPrefixTrigger ? "".concat(triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.trigger).concat(query) : query;
49
+ onTextInsert({
50
+ forceFocusOnEditor: forceFocusOnEditor,
51
+ setSelectionAt: setSelectionAt,
52
+ text: fullQuery
53
+ });
54
+ }, [triggerHandler, onTextInsert, query]);
55
+ if (!isOpen || !triggerHandler || !(decorationElement instanceof HTMLElement) || items.length === 0 && !errorInfo) {
56
+ return null;
57
+ }
58
+ return /*#__PURE__*/_react.default.createElement(_TypeAheadPopup.TypeAheadPopup, {
59
+ editorView: editorView,
60
+ popupsMountPoint: (_popupMountRef$curren = popupMountRef.current) === null || _popupMountRef$curren === void 0 ? void 0 : _popupMountRef$curren.popupsMountPoint,
61
+ popupsBoundariesElement: (_popupMountRef$curren2 = popupMountRef.current) === null || _popupMountRef$curren2 === void 0 ? void 0 : _popupMountRef$curren2.popupsBoundariesElement,
62
+ popupsScrollableElement: (_popupMountRef$curren3 = popupMountRef.current) === null || _popupMountRef$curren3 === void 0 ? void 0 : _popupMountRef$curren3.popupsScrollableElement,
63
+ anchorElement: decorationElement,
64
+ triggerHandler: triggerHandler,
65
+ items: items,
66
+ errorInfo: errorInfo
67
+ // selectedIndex={selectedIndex}
68
+ // setSelectedItem={setSelectedItem}
69
+ ,
70
+ onItemInsert: insertItem,
71
+ decorationSet: decorationSet,
72
+ isEmptyQuery: !query,
73
+ query: query,
74
+ cancel: cancel,
75
+ api: api
76
+ });
77
+ });
@@ -0,0 +1,282 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ var _typeof = require("@babel/runtime/helpers/typeof");
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.TypeAheadPopup = void 0;
9
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
+ var _react = _interopRequireWildcard(require("react"));
11
+ var _react2 = require("@emotion/react");
12
+ var _rafSchd = _interopRequireDefault(require("raf-schd"));
13
+ var _analytics = require("@atlaskit/editor-common/analytics");
14
+ var _ui = require("@atlaskit/editor-common/ui");
15
+ var _editorElementBrowser = require("@atlaskit/editor-element-browser");
16
+ var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
17
+ var _primitives = require("@atlaskit/primitives");
18
+ var _colors = require("@atlaskit/theme/colors");
19
+ var _constants = require("../../pm-plugins/constants");
20
+ var _TypeAheadErrorFallback = require("../TypeAheadErrorFallback");
21
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
22
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
23
+ /**
24
+ * @jsxRuntime classic
25
+ * @jsx jsx
26
+ */
27
+
28
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
29
+
30
+ var DEFAULT_TYPEAHEAD_MENU_HEIGHT = 520;
31
+ // const DEFAULT_TYPEAHEAD_MENU_HEIGHT_NEW = 480;
32
+
33
+ var ITEM_PADDING = 12;
34
+ var typeAheadContent = (0, _react2.css)({
35
+ background: "var(--ds-surface-overlay, ".concat(_colors.N0, ")"),
36
+ borderRadius: "var(--ds-border-radius, 3px)",
37
+ boxShadow: "var(--ds-shadow-overlay, ".concat("0 0 1px ".concat(_colors.N60A, ", 0 4px 8px -2px ").concat(_colors.N50A), ")"),
38
+ padding: "var(--ds-space-050, 4px)".concat(" 0"),
39
+ width: '280px',
40
+ maxHeight: '520px' /* ~5.5 visibile items */,
41
+ overflowY: 'auto',
42
+ MsOverflowStyle: '-ms-autohiding-scrollbar',
43
+ position: 'relative'
44
+ });
45
+ // const typeAheadContentOverride = css({
46
+ // maxHeight: `${DEFAULT_TYPEAHEAD_MENU_HEIGHT_NEW}px`,
47
+ // });
48
+
49
+ var viewAllButtonStyles = (0, _primitives.xcss)({
50
+ background: "var(--ds-background-input-pressed, #FFFFFF)",
51
+ position: 'sticky',
52
+ bottom: '-4px',
53
+ width: '100%',
54
+ height: '40px',
55
+ color: 'color.text.subtle',
56
+ fontWeight: "var(--ds-font-weight-medium, 500)",
57
+ ':hover': {
58
+ textDecoration: 'underline'
59
+ },
60
+ ':active': {
61
+ color: 'color.text'
62
+ }
63
+ });
64
+ var OFFSET = [0, 8];
65
+ var TypeAheadPopup = exports.TypeAheadPopup = /*#__PURE__*/_react.default.memo(function (props) {
66
+ var triggerHandler = props.triggerHandler,
67
+ anchorElement = props.anchorElement,
68
+ popupsMountPoint = props.popupsMountPoint,
69
+ popupsBoundariesElement = props.popupsBoundariesElement,
70
+ popupsScrollableElement = props.popupsScrollableElement,
71
+ items = props.items,
72
+ errorInfo = props.errorInfo,
73
+ onItemInsert = props.onItemInsert,
74
+ isEmptyQuery = props.isEmptyQuery,
75
+ cancel = props.cancel,
76
+ api = props.api,
77
+ query = props.query;
78
+ var ref = (0, _react.useRef)(null);
79
+ var defaultMenuHeight = DEFAULT_TYPEAHEAD_MENU_HEIGHT;
80
+ var startTime = (0, _react.useMemo)(function () {
81
+ return performance.now();
82
+ },
83
+ // In case those props changes
84
+ // we need to recreate the startTime
85
+ [items, isEmptyQuery, triggerHandler] // eslint-disable-line react-hooks/exhaustive-deps
86
+ );
87
+ (0, _react.useEffect)(function () {
88
+ var _api$analytics, _api$analytics2;
89
+ if (!(api !== null && api !== void 0 && (_api$analytics = api.analytics) !== null && _api$analytics !== void 0 && (_api$analytics = _api$analytics.actions) !== null && _api$analytics !== void 0 && _api$analytics.fireAnalyticsEvent)) {
90
+ return;
91
+ }
92
+ var stopTime = performance.now();
93
+ var time = stopTime - startTime;
94
+ api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 || (_api$analytics2 = _api$analytics2.actions) === null || _api$analytics2 === void 0 || _api$analytics2.fireAnalyticsEvent({
95
+ action: _analytics.ACTION.RENDERED,
96
+ actionSubject: _analytics.ACTION_SUBJECT.TYPEAHEAD,
97
+ eventType: _analytics.EVENT_TYPE.OPERATIONAL,
98
+ attributes: {
99
+ time: time,
100
+ items: items.length,
101
+ initial: isEmptyQuery
102
+ }
103
+ });
104
+ }, [startTime, items, isEmptyQuery,
105
+ // In case the current triggerHandler changes
106
+ // e.g: Inserting a mention using the quick insert
107
+ // we need to send the event again
108
+ // eslint-disable-next-line react-hooks/exhaustive-deps
109
+ triggerHandler, api]);
110
+
111
+ // useEffect(() => {
112
+ // if (!api?.analytics?.actions?.fireAnalyticsEvent) {
113
+ // return;
114
+ // }
115
+
116
+ // api?.analytics?.actions?.fireAnalyticsEvent({
117
+ // action: ACTION.VIEWED,
118
+ // actionSubject: ACTION_SUBJECT.TYPEAHEAD_ITEM,
119
+ // eventType: EVENT_TYPE.OPERATIONAL,
120
+ // attributes: {
121
+ // index: selectedIndex,
122
+ // items: items.length,
123
+ // },
124
+ // });
125
+ // }, [
126
+ // items,
127
+ // api,
128
+ // selectedIndex,
129
+ // // In case the current triggerHandler changes
130
+ // // e.g: Inserting a mention using the quick insert
131
+ // // we need to send the event again
132
+ // // eslint-disable-next-line react-hooks/exhaustive-deps
133
+ // triggerHandler,
134
+ // ]);
135
+
136
+ var _useState = (0, _react.useState)(defaultMenuHeight),
137
+ _useState2 = (0, _slicedToArray2.default)(_useState, 2),
138
+ fitHeight = _useState2[0],
139
+ setFitHeight = _useState2[1];
140
+ var getFitHeight = (0, _react.useCallback)(function () {
141
+ if (!anchorElement || !popupsMountPoint) {
142
+ return;
143
+ }
144
+ var target = anchorElement;
145
+ var _target$getBoundingCl = target.getBoundingClientRect(),
146
+ targetTop = _target$getBoundingCl.top,
147
+ targetHeight = _target$getBoundingCl.height;
148
+ var boundariesElement = popupsBoundariesElement || document.body;
149
+ var _boundariesElement$ge = boundariesElement.getBoundingClientRect(),
150
+ boundariesHeight = _boundariesElement$ge.height,
151
+ boundariesTop = _boundariesElement$ge.top;
152
+
153
+ // Calculating the space above and space below our decoration
154
+ var spaceAbove = targetTop - (boundariesTop - boundariesElement.scrollTop);
155
+ var spaceBelow = boundariesTop + boundariesHeight - (targetTop + targetHeight);
156
+
157
+ // Keep default height if more than enough space
158
+ if (spaceBelow >= defaultMenuHeight) {
159
+ return setFitHeight(defaultMenuHeight);
160
+ }
161
+
162
+ // Determines whether typeahead will fit above or below decoration
163
+ // and return the space available.
164
+ var newFitHeight = spaceBelow >= spaceAbove ? spaceBelow : spaceAbove;
165
+
166
+ // Each typeahead item has some padding
167
+ // We want to leave some space at the top so first item
168
+ // is not partially cropped
169
+ var fitHeightWithSpace = newFitHeight - ITEM_PADDING * 2;
170
+
171
+ // Ensure typeahead height is max its default height
172
+ var minFitHeight = Math.min(defaultMenuHeight, fitHeightWithSpace);
173
+ return setFitHeight(minFitHeight);
174
+ }, [anchorElement, defaultMenuHeight, popupsBoundariesElement, popupsMountPoint]);
175
+ var getFitHeightDebounced = (0, _rafSchd.default)(getFitHeight);
176
+ (0, _react.useLayoutEffect)(function () {
177
+ // Ignored via go/ees005
178
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
179
+ var scrollableElement = popupsScrollableElement || (0, _ui.findOverflowScrollParent)(anchorElement);
180
+ getFitHeight();
181
+ // Ignored via go/ees005
182
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
183
+ window.addEventListener('resize', getFitHeightDebounced);
184
+ if (scrollableElement) {
185
+ // Ignored via go/ees005
186
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
187
+ scrollableElement.addEventListener('scroll', getFitHeightDebounced);
188
+ }
189
+ return function () {
190
+ // Ignored via go/ees005
191
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
192
+ window.removeEventListener('resize', getFitHeightDebounced);
193
+ if (scrollableElement) {
194
+ // Ignored via go/ees005
195
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
196
+ scrollableElement.removeEventListener('scroll', getFitHeightDebounced);
197
+ }
198
+ };
199
+ }, [anchorElement, popupsScrollableElement, getFitHeightDebounced, getFitHeight]);
200
+ (0, _react.useLayoutEffect)(function () {
201
+ var focusOut = function focusOut(event) {
202
+ var _window$getSelection;
203
+ var relatedTarget = event.relatedTarget;
204
+
205
+ // Given the user is changing the focus
206
+ // When the target is inside the TypeAhead Popup
207
+ // Then the popup should stay open
208
+ if (relatedTarget instanceof HTMLElement && relatedTarget.closest && (relatedTarget.closest(".".concat(_constants.TYPE_AHEAD_POPUP_CONTENT_CLASS)) || relatedTarget.closest("[data-type-ahead=\"".concat(_constants.TYPE_AHEAD_DECORATION_DATA_ATTRIBUTE, "\"]")))) {
209
+ return;
210
+ }
211
+ if (!(((_window$getSelection = window.getSelection()) === null || _window$getSelection === void 0 ? void 0 : _window$getSelection.type) === 'Range')) {
212
+ return;
213
+ }
214
+ cancel({
215
+ addPrefixTrigger: true,
216
+ setSelectionAt: _constants.CloseSelectionOptions.AFTER_TEXT_INSERTED,
217
+ forceFocusOnEditor: false
218
+ });
219
+ };
220
+ var element = ref.current;
221
+ // Ignored via go/ees005
222
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
223
+ element === null || element === void 0 || element.addEventListener('focusout', focusOut);
224
+ return function () {
225
+ // Ignored via go/ees005
226
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
227
+ element === null || element === void 0 || element.removeEventListener('focusout', focusOut);
228
+ };
229
+ }, [ref, cancel]);
230
+
231
+ // ED-17443 When you press escape on typeahead panel, it should remove focus and close the panel
232
+ // This is the expected keyboard behaviour advised by the Accessibility team
233
+ (0, _react.useLayoutEffect)(function () {
234
+ var escape = function escape(event) {
235
+ if (event.key === 'Escape') {
236
+ cancel({
237
+ addPrefixTrigger: true,
238
+ setSelectionAt: _constants.CloseSelectionOptions.AFTER_TEXT_INSERTED,
239
+ forceFocusOnEditor: true
240
+ });
241
+ }
242
+ };
243
+ var element = ref.current;
244
+ // Ignored via go/ees005
245
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
246
+ element === null || element === void 0 || element.addEventListener('keydown', escape);
247
+ return function () {
248
+ // Ignored via go/ees005
249
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
250
+ element === null || element === void 0 || element.removeEventListener('keydown', escape);
251
+ };
252
+ }, [ref, cancel]);
253
+ return (0, _react2.jsx)(_ui.Popup, {
254
+ zIndex: _editorSharedStyles.akEditorFloatingDialogZIndex,
255
+ target: anchorElement,
256
+ mountTo: popupsMountPoint,
257
+ boundariesElement: popupsBoundariesElement,
258
+ scrollableElement: popupsScrollableElement,
259
+ fitHeight: fitHeight,
260
+ fitWidth: 280,
261
+ offset: OFFSET,
262
+ ariaLabel: null,
263
+ preventOverflow: true
264
+ }, (0, _react2.jsx)("div", {
265
+ css: [typeAheadContent]
266
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
267
+ ,
268
+ tabIndex: 0
269
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
270
+ ,
271
+ className: _constants.TYPE_AHEAD_POPUP_CONTENT_CLASS,
272
+ ref: ref
273
+ }, errorInfo ? (0, _react2.jsx)(_TypeAheadErrorFallback.TypeAheadErrorFallback, null) : (0, _react2.jsx)(_react.default.Fragment, null, (0, _react2.jsx)(_editorElementBrowser.QuickInsertPanel, {
274
+ items: items,
275
+ onItemInsert: onItemInsert,
276
+ query: query
277
+ }), (0, _react2.jsx)(_primitives.Pressable, {
278
+ xcss: viewAllButtonStyles
279
+ // onClick={() => api?.contextPanel?.actions.showPanel()}
280
+ }, "View all inserts"))));
281
+ });
282
+ TypeAheadPopup.displayName = 'TypeAheadPopup';
@@ -1,21 +1,36 @@
1
1
  import React from 'react';
2
2
  import { useSharedPluginState } from '@atlaskit/editor-common/hooks';
3
+ import { TypeAheadAvailableNodes } from '@atlaskit/editor-common/type-ahead';
4
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
5
+ import { TypeAheadMenu as TypeAheadMenuModern } from './modern/TypeAheadMenu';
3
6
  import { TypeAheadMenu } from './TypeAheadMenu';
4
7
  export function ContentComponent({
5
8
  api,
6
9
  editorView,
7
10
  popupMountRef
8
11
  }) {
12
+ var _typeAheadState$trigg;
9
13
  const {
10
14
  typeAheadState
11
15
  } = useSharedPluginState(api, ['typeAhead']);
12
16
  if (!typeAheadState) {
13
17
  return null;
14
18
  }
15
- return /*#__PURE__*/React.createElement(TypeAheadMenu, {
16
- editorView: editorView,
17
- popupMountRef: popupMountRef,
18
- typeAheadState: typeAheadState,
19
- api: api
20
- });
19
+ if (
20
+ // TODO: Also requires a check for editor appearance (needs to be enabled for full-page/full-width editor only ?)
21
+ !editorExperiment('platform_editor_controls', 'control') && ((_typeAheadState$trigg = typeAheadState.triggerHandler) === null || _typeAheadState$trigg === void 0 ? void 0 : _typeAheadState$trigg.id) === TypeAheadAvailableNodes.QUICK_INSERT) {
22
+ return /*#__PURE__*/React.createElement(TypeAheadMenuModern, {
23
+ editorView: editorView,
24
+ popupMountRef: popupMountRef,
25
+ typeAheadState: typeAheadState,
26
+ api: api
27
+ });
28
+ } else {
29
+ return /*#__PURE__*/React.createElement(TypeAheadMenu, {
30
+ editorView: editorView,
31
+ popupMountRef: popupMountRef,
32
+ typeAheadState: typeAheadState,
33
+ api: api
34
+ });
35
+ }
21
36
  }
@@ -0,0 +1,68 @@
1
+ import React from 'react';
2
+ import { SelectItemMode } from '@atlaskit/editor-common/type-ahead';
3
+ import { useItemInsert } from '../hooks/use-item-insert';
4
+ import { TypeAheadPopup as TypeAheadPopupModern } from './TypeAheadPopup';
5
+ export const TypeAheadMenu = /*#__PURE__*/React.memo(({
6
+ editorView,
7
+ popupMountRef,
8
+ typeAheadState,
9
+ api
10
+ }) => {
11
+ var _popupMountRef$curren, _popupMountRef$curren2, _popupMountRef$curren3;
12
+ const isOpen = typeAheadState.decorationSet.find().length > 0;
13
+ const {
14
+ triggerHandler,
15
+ items,
16
+ errorInfo,
17
+ decorationElement,
18
+ decorationSet,
19
+ query
20
+ } = typeAheadState;
21
+ const [onItemInsert, onTextInsert] = useItemInsert(
22
+ // Ignored via go/ees005
23
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
24
+ triggerHandler, editorView, items, api);
25
+ const insertItem = React.useCallback((mode = SelectItemMode.SELECTED, index) => {
26
+ queueMicrotask(() => {
27
+ onItemInsert({
28
+ mode,
29
+ index,
30
+ query
31
+ });
32
+ });
33
+ }, [onItemInsert, query]);
34
+ const cancel = React.useCallback(({
35
+ setSelectionAt,
36
+ addPrefixTrigger,
37
+ forceFocusOnEditor
38
+ }) => {
39
+ const fullQuery = addPrefixTrigger ? `${triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.trigger}${query}` : query;
40
+ onTextInsert({
41
+ forceFocusOnEditor,
42
+ setSelectionAt,
43
+ text: fullQuery
44
+ });
45
+ }, [triggerHandler, onTextInsert, query]);
46
+ if (!isOpen || !triggerHandler || !(decorationElement instanceof HTMLElement) || items.length === 0 && !errorInfo) {
47
+ return null;
48
+ }
49
+ return /*#__PURE__*/React.createElement(TypeAheadPopupModern, {
50
+ editorView: editorView,
51
+ popupsMountPoint: (_popupMountRef$curren = popupMountRef.current) === null || _popupMountRef$curren === void 0 ? void 0 : _popupMountRef$curren.popupsMountPoint,
52
+ popupsBoundariesElement: (_popupMountRef$curren2 = popupMountRef.current) === null || _popupMountRef$curren2 === void 0 ? void 0 : _popupMountRef$curren2.popupsBoundariesElement,
53
+ popupsScrollableElement: (_popupMountRef$curren3 = popupMountRef.current) === null || _popupMountRef$curren3 === void 0 ? void 0 : _popupMountRef$curren3.popupsScrollableElement,
54
+ anchorElement: decorationElement,
55
+ triggerHandler: triggerHandler,
56
+ items: items,
57
+ errorInfo: errorInfo
58
+ // selectedIndex={selectedIndex}
59
+ // setSelectedItem={setSelectedItem}
60
+ ,
61
+ onItemInsert: insertItem,
62
+ decorationSet: decorationSet,
63
+ isEmptyQuery: !query,
64
+ query: query,
65
+ cancel: cancel,
66
+ api: api
67
+ });
68
+ });
@@ -0,0 +1,275 @@
1
+ /**
2
+ * @jsxRuntime classic
3
+ * @jsx jsx
4
+ */
5
+ import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
6
+
7
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
8
+ import { css, jsx } from '@emotion/react';
9
+ import rafSchedule from 'raf-schd';
10
+ import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
11
+ import { findOverflowScrollParent, Popup } from '@atlaskit/editor-common/ui';
12
+ import { QuickInsertPanel } from '@atlaskit/editor-element-browser';
13
+ import { akEditorFloatingDialogZIndex } from '@atlaskit/editor-shared-styles';
14
+ import { Pressable, xcss } from '@atlaskit/primitives';
15
+ import { N0, N50A, N60A } from '@atlaskit/theme/colors';
16
+ import { CloseSelectionOptions, TYPE_AHEAD_DECORATION_DATA_ATTRIBUTE, TYPE_AHEAD_POPUP_CONTENT_CLASS } from '../../pm-plugins/constants';
17
+ import { TypeAheadErrorFallback } from '../TypeAheadErrorFallback';
18
+ const DEFAULT_TYPEAHEAD_MENU_HEIGHT = 520;
19
+ // const DEFAULT_TYPEAHEAD_MENU_HEIGHT_NEW = 480;
20
+
21
+ const ITEM_PADDING = 12;
22
+ const typeAheadContent = css({
23
+ background: `var(--ds-surface-overlay, ${N0})`,
24
+ borderRadius: "var(--ds-border-radius, 3px)",
25
+ boxShadow: `var(--ds-shadow-overlay, ${`0 0 1px ${N60A}, 0 4px 8px -2px ${N50A}`})`,
26
+ padding: `${"var(--ds-space-050, 4px)"} 0`,
27
+ width: '280px',
28
+ maxHeight: '520px' /* ~5.5 visibile items */,
29
+ overflowY: 'auto',
30
+ MsOverflowStyle: '-ms-autohiding-scrollbar',
31
+ position: 'relative'
32
+ });
33
+ // const typeAheadContentOverride = css({
34
+ // maxHeight: `${DEFAULT_TYPEAHEAD_MENU_HEIGHT_NEW}px`,
35
+ // });
36
+
37
+ const viewAllButtonStyles = xcss({
38
+ background: "var(--ds-background-input-pressed, #FFFFFF)",
39
+ position: 'sticky',
40
+ bottom: '-4px',
41
+ width: '100%',
42
+ height: '40px',
43
+ color: 'color.text.subtle',
44
+ fontWeight: "var(--ds-font-weight-medium, 500)",
45
+ ':hover': {
46
+ textDecoration: 'underline'
47
+ },
48
+ ':active': {
49
+ color: 'color.text'
50
+ }
51
+ });
52
+ const OFFSET = [0, 8];
53
+ export const TypeAheadPopup = /*#__PURE__*/React.memo(props => {
54
+ const {
55
+ triggerHandler,
56
+ anchorElement,
57
+ popupsMountPoint,
58
+ popupsBoundariesElement,
59
+ popupsScrollableElement,
60
+ items,
61
+ errorInfo,
62
+ onItemInsert,
63
+ isEmptyQuery,
64
+ cancel,
65
+ api,
66
+ query
67
+ } = props;
68
+ const ref = useRef(null);
69
+ const defaultMenuHeight = DEFAULT_TYPEAHEAD_MENU_HEIGHT;
70
+ const startTime = useMemo(() => performance.now(),
71
+ // In case those props changes
72
+ // we need to recreate the startTime
73
+ [items, isEmptyQuery, triggerHandler] // eslint-disable-line react-hooks/exhaustive-deps
74
+ );
75
+ useEffect(() => {
76
+ var _api$analytics, _api$analytics$action, _api$analytics2, _api$analytics2$actio;
77
+ if (!(api !== null && api !== void 0 && (_api$analytics = api.analytics) !== null && _api$analytics !== void 0 && (_api$analytics$action = _api$analytics.actions) !== null && _api$analytics$action !== void 0 && _api$analytics$action.fireAnalyticsEvent)) {
78
+ return;
79
+ }
80
+ const stopTime = performance.now();
81
+ const time = stopTime - startTime;
82
+ api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : (_api$analytics2$actio = _api$analytics2.actions) === null || _api$analytics2$actio === void 0 ? void 0 : _api$analytics2$actio.fireAnalyticsEvent({
83
+ action: ACTION.RENDERED,
84
+ actionSubject: ACTION_SUBJECT.TYPEAHEAD,
85
+ eventType: EVENT_TYPE.OPERATIONAL,
86
+ attributes: {
87
+ time,
88
+ items: items.length,
89
+ initial: isEmptyQuery
90
+ }
91
+ });
92
+ }, [startTime, items, isEmptyQuery,
93
+ // In case the current triggerHandler changes
94
+ // e.g: Inserting a mention using the quick insert
95
+ // we need to send the event again
96
+ // eslint-disable-next-line react-hooks/exhaustive-deps
97
+ triggerHandler, api]);
98
+
99
+ // useEffect(() => {
100
+ // if (!api?.analytics?.actions?.fireAnalyticsEvent) {
101
+ // return;
102
+ // }
103
+
104
+ // api?.analytics?.actions?.fireAnalyticsEvent({
105
+ // action: ACTION.VIEWED,
106
+ // actionSubject: ACTION_SUBJECT.TYPEAHEAD_ITEM,
107
+ // eventType: EVENT_TYPE.OPERATIONAL,
108
+ // attributes: {
109
+ // index: selectedIndex,
110
+ // items: items.length,
111
+ // },
112
+ // });
113
+ // }, [
114
+ // items,
115
+ // api,
116
+ // selectedIndex,
117
+ // // In case the current triggerHandler changes
118
+ // // e.g: Inserting a mention using the quick insert
119
+ // // we need to send the event again
120
+ // // eslint-disable-next-line react-hooks/exhaustive-deps
121
+ // triggerHandler,
122
+ // ]);
123
+
124
+ const [fitHeight, setFitHeight] = useState(defaultMenuHeight);
125
+ const getFitHeight = useCallback(() => {
126
+ if (!anchorElement || !popupsMountPoint) {
127
+ return;
128
+ }
129
+ const target = anchorElement;
130
+ const {
131
+ top: targetTop,
132
+ height: targetHeight
133
+ } = target.getBoundingClientRect();
134
+ const boundariesElement = popupsBoundariesElement || document.body;
135
+ const {
136
+ height: boundariesHeight,
137
+ top: boundariesTop
138
+ } = boundariesElement.getBoundingClientRect();
139
+
140
+ // Calculating the space above and space below our decoration
141
+ const spaceAbove = targetTop - (boundariesTop - boundariesElement.scrollTop);
142
+ const spaceBelow = boundariesTop + boundariesHeight - (targetTop + targetHeight);
143
+
144
+ // Keep default height if more than enough space
145
+ if (spaceBelow >= defaultMenuHeight) {
146
+ return setFitHeight(defaultMenuHeight);
147
+ }
148
+
149
+ // Determines whether typeahead will fit above or below decoration
150
+ // and return the space available.
151
+ const newFitHeight = spaceBelow >= spaceAbove ? spaceBelow : spaceAbove;
152
+
153
+ // Each typeahead item has some padding
154
+ // We want to leave some space at the top so first item
155
+ // is not partially cropped
156
+ const fitHeightWithSpace = newFitHeight - ITEM_PADDING * 2;
157
+
158
+ // Ensure typeahead height is max its default height
159
+ const minFitHeight = Math.min(defaultMenuHeight, fitHeightWithSpace);
160
+ return setFitHeight(minFitHeight);
161
+ }, [anchorElement, defaultMenuHeight, popupsBoundariesElement, popupsMountPoint]);
162
+ const getFitHeightDebounced = rafSchedule(getFitHeight);
163
+ useLayoutEffect(() => {
164
+ // Ignored via go/ees005
165
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
166
+ const scrollableElement = popupsScrollableElement || findOverflowScrollParent(anchorElement);
167
+ getFitHeight();
168
+ // Ignored via go/ees005
169
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
170
+ window.addEventListener('resize', getFitHeightDebounced);
171
+ if (scrollableElement) {
172
+ // Ignored via go/ees005
173
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
174
+ scrollableElement.addEventListener('scroll', getFitHeightDebounced);
175
+ }
176
+ return () => {
177
+ // Ignored via go/ees005
178
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
179
+ window.removeEventListener('resize', getFitHeightDebounced);
180
+ if (scrollableElement) {
181
+ // Ignored via go/ees005
182
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
183
+ scrollableElement.removeEventListener('scroll', getFitHeightDebounced);
184
+ }
185
+ };
186
+ }, [anchorElement, popupsScrollableElement, getFitHeightDebounced, getFitHeight]);
187
+ useLayoutEffect(() => {
188
+ const focusOut = event => {
189
+ var _window$getSelection;
190
+ const {
191
+ relatedTarget
192
+ } = event;
193
+
194
+ // Given the user is changing the focus
195
+ // When the target is inside the TypeAhead Popup
196
+ // Then the popup should stay open
197
+ if (relatedTarget instanceof HTMLElement && relatedTarget.closest && (relatedTarget.closest(`.${TYPE_AHEAD_POPUP_CONTENT_CLASS}`) || relatedTarget.closest(`[data-type-ahead="${TYPE_AHEAD_DECORATION_DATA_ATTRIBUTE}"]`))) {
198
+ return;
199
+ }
200
+ if (!(((_window$getSelection = window.getSelection()) === null || _window$getSelection === void 0 ? void 0 : _window$getSelection.type) === 'Range')) {
201
+ return;
202
+ }
203
+ cancel({
204
+ addPrefixTrigger: true,
205
+ setSelectionAt: CloseSelectionOptions.AFTER_TEXT_INSERTED,
206
+ forceFocusOnEditor: false
207
+ });
208
+ };
209
+ const {
210
+ current: element
211
+ } = ref;
212
+ // Ignored via go/ees005
213
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
214
+ element === null || element === void 0 ? void 0 : element.addEventListener('focusout', focusOut);
215
+ return () => {
216
+ // Ignored via go/ees005
217
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
218
+ element === null || element === void 0 ? void 0 : element.removeEventListener('focusout', focusOut);
219
+ };
220
+ }, [ref, cancel]);
221
+
222
+ // ED-17443 When you press escape on typeahead panel, it should remove focus and close the panel
223
+ // This is the expected keyboard behaviour advised by the Accessibility team
224
+ useLayoutEffect(() => {
225
+ const escape = event => {
226
+ if (event.key === 'Escape') {
227
+ cancel({
228
+ addPrefixTrigger: true,
229
+ setSelectionAt: CloseSelectionOptions.AFTER_TEXT_INSERTED,
230
+ forceFocusOnEditor: true
231
+ });
232
+ }
233
+ };
234
+ const {
235
+ current: element
236
+ } = ref;
237
+ // Ignored via go/ees005
238
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
239
+ element === null || element === void 0 ? void 0 : element.addEventListener('keydown', escape);
240
+ return () => {
241
+ // Ignored via go/ees005
242
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
243
+ element === null || element === void 0 ? void 0 : element.removeEventListener('keydown', escape);
244
+ };
245
+ }, [ref, cancel]);
246
+ return jsx(Popup, {
247
+ zIndex: akEditorFloatingDialogZIndex,
248
+ target: anchorElement,
249
+ mountTo: popupsMountPoint,
250
+ boundariesElement: popupsBoundariesElement,
251
+ scrollableElement: popupsScrollableElement,
252
+ fitHeight: fitHeight,
253
+ fitWidth: 280,
254
+ offset: OFFSET,
255
+ ariaLabel: null,
256
+ preventOverflow: true
257
+ }, jsx("div", {
258
+ css: [typeAheadContent]
259
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
260
+ ,
261
+ tabIndex: 0
262
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
263
+ ,
264
+ className: TYPE_AHEAD_POPUP_CONTENT_CLASS,
265
+ ref: ref
266
+ }, errorInfo ? jsx(TypeAheadErrorFallback, null) : jsx(React.Fragment, null, jsx(QuickInsertPanel, {
267
+ items: items,
268
+ onItemInsert: onItemInsert,
269
+ query: query
270
+ }), jsx(Pressable, {
271
+ xcss: viewAllButtonStyles
272
+ // onClick={() => api?.contextPanel?.actions.showPanel()}
273
+ }, "View all inserts"))));
274
+ });
275
+ TypeAheadPopup.displayName = 'TypeAheadPopup';
@@ -1,7 +1,11 @@
1
1
  import React from 'react';
2
2
  import { useSharedPluginState } from '@atlaskit/editor-common/hooks';
3
+ import { TypeAheadAvailableNodes } from '@atlaskit/editor-common/type-ahead';
4
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
5
+ import { TypeAheadMenu as TypeAheadMenuModern } from './modern/TypeAheadMenu';
3
6
  import { TypeAheadMenu } from './TypeAheadMenu';
4
7
  export function ContentComponent(_ref) {
8
+ var _typeAheadState$trigg;
5
9
  var api = _ref.api,
6
10
  editorView = _ref.editorView,
7
11
  popupMountRef = _ref.popupMountRef;
@@ -10,10 +14,21 @@ export function ContentComponent(_ref) {
10
14
  if (!typeAheadState) {
11
15
  return null;
12
16
  }
13
- return /*#__PURE__*/React.createElement(TypeAheadMenu, {
14
- editorView: editorView,
15
- popupMountRef: popupMountRef,
16
- typeAheadState: typeAheadState,
17
- api: api
18
- });
17
+ if (
18
+ // TODO: Also requires a check for editor appearance (needs to be enabled for full-page/full-width editor only ?)
19
+ !editorExperiment('platform_editor_controls', 'control') && ((_typeAheadState$trigg = typeAheadState.triggerHandler) === null || _typeAheadState$trigg === void 0 ? void 0 : _typeAheadState$trigg.id) === TypeAheadAvailableNodes.QUICK_INSERT) {
20
+ return /*#__PURE__*/React.createElement(TypeAheadMenuModern, {
21
+ editorView: editorView,
22
+ popupMountRef: popupMountRef,
23
+ typeAheadState: typeAheadState,
24
+ api: api
25
+ });
26
+ } else {
27
+ return /*#__PURE__*/React.createElement(TypeAheadMenu, {
28
+ editorView: editorView,
29
+ popupMountRef: popupMountRef,
30
+ typeAheadState: typeAheadState,
31
+ api: api
32
+ });
33
+ }
19
34
  }
@@ -0,0 +1,70 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import React from 'react';
3
+ import { SelectItemMode } from '@atlaskit/editor-common/type-ahead';
4
+ import { useItemInsert } from '../hooks/use-item-insert';
5
+ import { TypeAheadPopup as TypeAheadPopupModern } from './TypeAheadPopup';
6
+ export var TypeAheadMenu = /*#__PURE__*/React.memo(function (_ref) {
7
+ var _popupMountRef$curren, _popupMountRef$curren2, _popupMountRef$curren3;
8
+ var editorView = _ref.editorView,
9
+ popupMountRef = _ref.popupMountRef,
10
+ typeAheadState = _ref.typeAheadState,
11
+ api = _ref.api;
12
+ var isOpen = typeAheadState.decorationSet.find().length > 0;
13
+ var triggerHandler = typeAheadState.triggerHandler,
14
+ items = typeAheadState.items,
15
+ errorInfo = typeAheadState.errorInfo,
16
+ decorationElement = typeAheadState.decorationElement,
17
+ decorationSet = typeAheadState.decorationSet,
18
+ query = typeAheadState.query;
19
+ var _useItemInsert = useItemInsert(
20
+ // Ignored via go/ees005
21
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
22
+ triggerHandler, editorView, items, api),
23
+ _useItemInsert2 = _slicedToArray(_useItemInsert, 2),
24
+ onItemInsert = _useItemInsert2[0],
25
+ onTextInsert = _useItemInsert2[1];
26
+ var insertItem = React.useCallback(function () {
27
+ var mode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : SelectItemMode.SELECTED;
28
+ var index = arguments.length > 1 ? arguments[1] : undefined;
29
+ queueMicrotask(function () {
30
+ onItemInsert({
31
+ mode: mode,
32
+ index: index,
33
+ query: query
34
+ });
35
+ });
36
+ }, [onItemInsert, query]);
37
+ var cancel = React.useCallback(function (_ref2) {
38
+ var setSelectionAt = _ref2.setSelectionAt,
39
+ addPrefixTrigger = _ref2.addPrefixTrigger,
40
+ forceFocusOnEditor = _ref2.forceFocusOnEditor;
41
+ var fullQuery = addPrefixTrigger ? "".concat(triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.trigger).concat(query) : query;
42
+ onTextInsert({
43
+ forceFocusOnEditor: forceFocusOnEditor,
44
+ setSelectionAt: setSelectionAt,
45
+ text: fullQuery
46
+ });
47
+ }, [triggerHandler, onTextInsert, query]);
48
+ if (!isOpen || !triggerHandler || !(decorationElement instanceof HTMLElement) || items.length === 0 && !errorInfo) {
49
+ return null;
50
+ }
51
+ return /*#__PURE__*/React.createElement(TypeAheadPopupModern, {
52
+ editorView: editorView,
53
+ popupsMountPoint: (_popupMountRef$curren = popupMountRef.current) === null || _popupMountRef$curren === void 0 ? void 0 : _popupMountRef$curren.popupsMountPoint,
54
+ popupsBoundariesElement: (_popupMountRef$curren2 = popupMountRef.current) === null || _popupMountRef$curren2 === void 0 ? void 0 : _popupMountRef$curren2.popupsBoundariesElement,
55
+ popupsScrollableElement: (_popupMountRef$curren3 = popupMountRef.current) === null || _popupMountRef$curren3 === void 0 ? void 0 : _popupMountRef$curren3.popupsScrollableElement,
56
+ anchorElement: decorationElement,
57
+ triggerHandler: triggerHandler,
58
+ items: items,
59
+ errorInfo: errorInfo
60
+ // selectedIndex={selectedIndex}
61
+ // setSelectedItem={setSelectedItem}
62
+ ,
63
+ onItemInsert: insertItem,
64
+ decorationSet: decorationSet,
65
+ isEmptyQuery: !query,
66
+ query: query,
67
+ cancel: cancel,
68
+ api: api
69
+ });
70
+ });
@@ -0,0 +1,271 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ /**
3
+ * @jsxRuntime classic
4
+ * @jsx jsx
5
+ */
6
+ import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
7
+
8
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
9
+ import { css, jsx } from '@emotion/react';
10
+ import rafSchedule from 'raf-schd';
11
+ import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
12
+ import { findOverflowScrollParent, Popup } from '@atlaskit/editor-common/ui';
13
+ import { QuickInsertPanel } from '@atlaskit/editor-element-browser';
14
+ import { akEditorFloatingDialogZIndex } from '@atlaskit/editor-shared-styles';
15
+ import { Pressable, xcss } from '@atlaskit/primitives';
16
+ import { N0, N50A, N60A } from '@atlaskit/theme/colors';
17
+ import { CloseSelectionOptions, TYPE_AHEAD_DECORATION_DATA_ATTRIBUTE, TYPE_AHEAD_POPUP_CONTENT_CLASS } from '../../pm-plugins/constants';
18
+ import { TypeAheadErrorFallback } from '../TypeAheadErrorFallback';
19
+ var DEFAULT_TYPEAHEAD_MENU_HEIGHT = 520;
20
+ // const DEFAULT_TYPEAHEAD_MENU_HEIGHT_NEW = 480;
21
+
22
+ var ITEM_PADDING = 12;
23
+ var typeAheadContent = css({
24
+ background: "var(--ds-surface-overlay, ".concat(N0, ")"),
25
+ borderRadius: "var(--ds-border-radius, 3px)",
26
+ boxShadow: "var(--ds-shadow-overlay, ".concat("0 0 1px ".concat(N60A, ", 0 4px 8px -2px ").concat(N50A), ")"),
27
+ padding: "var(--ds-space-050, 4px)".concat(" 0"),
28
+ width: '280px',
29
+ maxHeight: '520px' /* ~5.5 visibile items */,
30
+ overflowY: 'auto',
31
+ MsOverflowStyle: '-ms-autohiding-scrollbar',
32
+ position: 'relative'
33
+ });
34
+ // const typeAheadContentOverride = css({
35
+ // maxHeight: `${DEFAULT_TYPEAHEAD_MENU_HEIGHT_NEW}px`,
36
+ // });
37
+
38
+ var viewAllButtonStyles = xcss({
39
+ background: "var(--ds-background-input-pressed, #FFFFFF)",
40
+ position: 'sticky',
41
+ bottom: '-4px',
42
+ width: '100%',
43
+ height: '40px',
44
+ color: 'color.text.subtle',
45
+ fontWeight: "var(--ds-font-weight-medium, 500)",
46
+ ':hover': {
47
+ textDecoration: 'underline'
48
+ },
49
+ ':active': {
50
+ color: 'color.text'
51
+ }
52
+ });
53
+ var OFFSET = [0, 8];
54
+ export var TypeAheadPopup = /*#__PURE__*/React.memo(function (props) {
55
+ var triggerHandler = props.triggerHandler,
56
+ anchorElement = props.anchorElement,
57
+ popupsMountPoint = props.popupsMountPoint,
58
+ popupsBoundariesElement = props.popupsBoundariesElement,
59
+ popupsScrollableElement = props.popupsScrollableElement,
60
+ items = props.items,
61
+ errorInfo = props.errorInfo,
62
+ onItemInsert = props.onItemInsert,
63
+ isEmptyQuery = props.isEmptyQuery,
64
+ cancel = props.cancel,
65
+ api = props.api,
66
+ query = props.query;
67
+ var ref = useRef(null);
68
+ var defaultMenuHeight = DEFAULT_TYPEAHEAD_MENU_HEIGHT;
69
+ var startTime = useMemo(function () {
70
+ return performance.now();
71
+ },
72
+ // In case those props changes
73
+ // we need to recreate the startTime
74
+ [items, isEmptyQuery, triggerHandler] // eslint-disable-line react-hooks/exhaustive-deps
75
+ );
76
+ useEffect(function () {
77
+ var _api$analytics, _api$analytics2;
78
+ if (!(api !== null && api !== void 0 && (_api$analytics = api.analytics) !== null && _api$analytics !== void 0 && (_api$analytics = _api$analytics.actions) !== null && _api$analytics !== void 0 && _api$analytics.fireAnalyticsEvent)) {
79
+ return;
80
+ }
81
+ var stopTime = performance.now();
82
+ var time = stopTime - startTime;
83
+ api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 || (_api$analytics2 = _api$analytics2.actions) === null || _api$analytics2 === void 0 || _api$analytics2.fireAnalyticsEvent({
84
+ action: ACTION.RENDERED,
85
+ actionSubject: ACTION_SUBJECT.TYPEAHEAD,
86
+ eventType: EVENT_TYPE.OPERATIONAL,
87
+ attributes: {
88
+ time: time,
89
+ items: items.length,
90
+ initial: isEmptyQuery
91
+ }
92
+ });
93
+ }, [startTime, items, isEmptyQuery,
94
+ // In case the current triggerHandler changes
95
+ // e.g: Inserting a mention using the quick insert
96
+ // we need to send the event again
97
+ // eslint-disable-next-line react-hooks/exhaustive-deps
98
+ triggerHandler, api]);
99
+
100
+ // useEffect(() => {
101
+ // if (!api?.analytics?.actions?.fireAnalyticsEvent) {
102
+ // return;
103
+ // }
104
+
105
+ // api?.analytics?.actions?.fireAnalyticsEvent({
106
+ // action: ACTION.VIEWED,
107
+ // actionSubject: ACTION_SUBJECT.TYPEAHEAD_ITEM,
108
+ // eventType: EVENT_TYPE.OPERATIONAL,
109
+ // attributes: {
110
+ // index: selectedIndex,
111
+ // items: items.length,
112
+ // },
113
+ // });
114
+ // }, [
115
+ // items,
116
+ // api,
117
+ // selectedIndex,
118
+ // // In case the current triggerHandler changes
119
+ // // e.g: Inserting a mention using the quick insert
120
+ // // we need to send the event again
121
+ // // eslint-disable-next-line react-hooks/exhaustive-deps
122
+ // triggerHandler,
123
+ // ]);
124
+
125
+ var _useState = useState(defaultMenuHeight),
126
+ _useState2 = _slicedToArray(_useState, 2),
127
+ fitHeight = _useState2[0],
128
+ setFitHeight = _useState2[1];
129
+ var getFitHeight = useCallback(function () {
130
+ if (!anchorElement || !popupsMountPoint) {
131
+ return;
132
+ }
133
+ var target = anchorElement;
134
+ var _target$getBoundingCl = target.getBoundingClientRect(),
135
+ targetTop = _target$getBoundingCl.top,
136
+ targetHeight = _target$getBoundingCl.height;
137
+ var boundariesElement = popupsBoundariesElement || document.body;
138
+ var _boundariesElement$ge = boundariesElement.getBoundingClientRect(),
139
+ boundariesHeight = _boundariesElement$ge.height,
140
+ boundariesTop = _boundariesElement$ge.top;
141
+
142
+ // Calculating the space above and space below our decoration
143
+ var spaceAbove = targetTop - (boundariesTop - boundariesElement.scrollTop);
144
+ var spaceBelow = boundariesTop + boundariesHeight - (targetTop + targetHeight);
145
+
146
+ // Keep default height if more than enough space
147
+ if (spaceBelow >= defaultMenuHeight) {
148
+ return setFitHeight(defaultMenuHeight);
149
+ }
150
+
151
+ // Determines whether typeahead will fit above or below decoration
152
+ // and return the space available.
153
+ var newFitHeight = spaceBelow >= spaceAbove ? spaceBelow : spaceAbove;
154
+
155
+ // Each typeahead item has some padding
156
+ // We want to leave some space at the top so first item
157
+ // is not partially cropped
158
+ var fitHeightWithSpace = newFitHeight - ITEM_PADDING * 2;
159
+
160
+ // Ensure typeahead height is max its default height
161
+ var minFitHeight = Math.min(defaultMenuHeight, fitHeightWithSpace);
162
+ return setFitHeight(minFitHeight);
163
+ }, [anchorElement, defaultMenuHeight, popupsBoundariesElement, popupsMountPoint]);
164
+ var getFitHeightDebounced = rafSchedule(getFitHeight);
165
+ useLayoutEffect(function () {
166
+ // Ignored via go/ees005
167
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
168
+ var scrollableElement = popupsScrollableElement || findOverflowScrollParent(anchorElement);
169
+ getFitHeight();
170
+ // Ignored via go/ees005
171
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
172
+ window.addEventListener('resize', getFitHeightDebounced);
173
+ if (scrollableElement) {
174
+ // Ignored via go/ees005
175
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
176
+ scrollableElement.addEventListener('scroll', getFitHeightDebounced);
177
+ }
178
+ return function () {
179
+ // Ignored via go/ees005
180
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
181
+ window.removeEventListener('resize', getFitHeightDebounced);
182
+ if (scrollableElement) {
183
+ // Ignored via go/ees005
184
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
185
+ scrollableElement.removeEventListener('scroll', getFitHeightDebounced);
186
+ }
187
+ };
188
+ }, [anchorElement, popupsScrollableElement, getFitHeightDebounced, getFitHeight]);
189
+ useLayoutEffect(function () {
190
+ var focusOut = function focusOut(event) {
191
+ var _window$getSelection;
192
+ var relatedTarget = event.relatedTarget;
193
+
194
+ // Given the user is changing the focus
195
+ // When the target is inside the TypeAhead Popup
196
+ // Then the popup should stay open
197
+ if (relatedTarget instanceof HTMLElement && relatedTarget.closest && (relatedTarget.closest(".".concat(TYPE_AHEAD_POPUP_CONTENT_CLASS)) || relatedTarget.closest("[data-type-ahead=\"".concat(TYPE_AHEAD_DECORATION_DATA_ATTRIBUTE, "\"]")))) {
198
+ return;
199
+ }
200
+ if (!(((_window$getSelection = window.getSelection()) === null || _window$getSelection === void 0 ? void 0 : _window$getSelection.type) === 'Range')) {
201
+ return;
202
+ }
203
+ cancel({
204
+ addPrefixTrigger: true,
205
+ setSelectionAt: CloseSelectionOptions.AFTER_TEXT_INSERTED,
206
+ forceFocusOnEditor: false
207
+ });
208
+ };
209
+ var element = ref.current;
210
+ // Ignored via go/ees005
211
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
212
+ element === null || element === void 0 || element.addEventListener('focusout', focusOut);
213
+ return function () {
214
+ // Ignored via go/ees005
215
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
216
+ element === null || element === void 0 || element.removeEventListener('focusout', focusOut);
217
+ };
218
+ }, [ref, cancel]);
219
+
220
+ // ED-17443 When you press escape on typeahead panel, it should remove focus and close the panel
221
+ // This is the expected keyboard behaviour advised by the Accessibility team
222
+ useLayoutEffect(function () {
223
+ var escape = function escape(event) {
224
+ if (event.key === 'Escape') {
225
+ cancel({
226
+ addPrefixTrigger: true,
227
+ setSelectionAt: CloseSelectionOptions.AFTER_TEXT_INSERTED,
228
+ forceFocusOnEditor: true
229
+ });
230
+ }
231
+ };
232
+ var element = ref.current;
233
+ // Ignored via go/ees005
234
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
235
+ element === null || element === void 0 || element.addEventListener('keydown', escape);
236
+ return function () {
237
+ // Ignored via go/ees005
238
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
239
+ element === null || element === void 0 || element.removeEventListener('keydown', escape);
240
+ };
241
+ }, [ref, cancel]);
242
+ return jsx(Popup, {
243
+ zIndex: akEditorFloatingDialogZIndex,
244
+ target: anchorElement,
245
+ mountTo: popupsMountPoint,
246
+ boundariesElement: popupsBoundariesElement,
247
+ scrollableElement: popupsScrollableElement,
248
+ fitHeight: fitHeight,
249
+ fitWidth: 280,
250
+ offset: OFFSET,
251
+ ariaLabel: null,
252
+ preventOverflow: true
253
+ }, jsx("div", {
254
+ css: [typeAheadContent]
255
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
256
+ ,
257
+ tabIndex: 0
258
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
259
+ ,
260
+ className: TYPE_AHEAD_POPUP_CONTENT_CLASS,
261
+ ref: ref
262
+ }, errorInfo ? jsx(TypeAheadErrorFallback, null) : jsx(React.Fragment, null, jsx(QuickInsertPanel, {
263
+ items: items,
264
+ onItemInsert: onItemInsert,
265
+ query: query
266
+ }), jsx(Pressable, {
267
+ xcss: viewAllButtonStyles
268
+ // onClick={() => api?.contextPanel?.actions.showPanel()}
269
+ }, "View all inserts"))));
270
+ });
271
+ TypeAheadPopup.displayName = 'TypeAheadPopup';
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
+ import type { EditorView } from '@atlaskit/editor-prosemirror/view';
4
+ import type { TypeAheadPlugin } from '../../typeAheadPluginType';
5
+ import type { PopupMountPointReference, TypeAheadPluginSharedState } from '../../types';
6
+ type TypeAheadMenuType = {
7
+ typeAheadState: TypeAheadPluginSharedState;
8
+ editorView: EditorView;
9
+ popupMountRef: PopupMountPointReference;
10
+ api: ExtractInjectionAPI<TypeAheadPlugin> | undefined;
11
+ };
12
+ export declare const TypeAheadMenu: React.MemoExoticComponent<({ editorView, popupMountRef, typeAheadState, api }: TypeAheadMenuType) => React.JSX.Element | null>;
13
+ export {};
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @jsxRuntime classic
3
+ * @jsx jsx
4
+ */
5
+ import React from 'react';
6
+ import { jsx } from '@emotion/react';
7
+ import type { SelectItemMode } from '@atlaskit/editor-common/type-ahead';
8
+ import type { ExtractInjectionAPI, TypeAheadItem, TypeAheadHandler } from '@atlaskit/editor-common/types';
9
+ import type { DecorationSet, EditorView } from '@atlaskit/editor-prosemirror/view';
10
+ import { CloseSelectionOptions } from '../../pm-plugins/constants';
11
+ import type { TypeAheadPlugin } from '../../typeAheadPluginType';
12
+ import type { TypeAheadErrorInfo } from '../../types';
13
+ type TypeAheadPopupProps = {
14
+ triggerHandler: TypeAheadHandler;
15
+ editorView: EditorView;
16
+ anchorElement: HTMLElement;
17
+ popupsMountPoint?: HTMLElement;
18
+ popupsBoundariesElement?: HTMLElement;
19
+ popupsScrollableElement?: HTMLElement;
20
+ items: Array<TypeAheadItem>;
21
+ errorInfo: TypeAheadErrorInfo;
22
+ decorationSet: DecorationSet;
23
+ isEmptyQuery: boolean;
24
+ query: string;
25
+ onItemInsert: (mode: SelectItemMode, index: number) => void;
26
+ cancel: (params: {
27
+ setSelectionAt: CloseSelectionOptions;
28
+ addPrefixTrigger: boolean;
29
+ forceFocusOnEditor: boolean;
30
+ }) => void;
31
+ api: ExtractInjectionAPI<TypeAheadPlugin> | undefined;
32
+ };
33
+ export declare const TypeAheadPopup: React.MemoExoticComponent<(props: TypeAheadPopupProps) => jsx.JSX.Element>;
34
+ export {};
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
+ import type { EditorView } from '@atlaskit/editor-prosemirror/view';
4
+ import type { TypeAheadPlugin } from '../../typeAheadPluginType';
5
+ import type { PopupMountPointReference, TypeAheadPluginSharedState } from '../../types';
6
+ type TypeAheadMenuType = {
7
+ typeAheadState: TypeAheadPluginSharedState;
8
+ editorView: EditorView;
9
+ popupMountRef: PopupMountPointReference;
10
+ api: ExtractInjectionAPI<TypeAheadPlugin> | undefined;
11
+ };
12
+ export declare const TypeAheadMenu: React.MemoExoticComponent<({ editorView, popupMountRef, typeAheadState, api }: TypeAheadMenuType) => React.JSX.Element | null>;
13
+ export {};
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @jsxRuntime classic
3
+ * @jsx jsx
4
+ */
5
+ import React from 'react';
6
+ import { jsx } from '@emotion/react';
7
+ import type { SelectItemMode } from '@atlaskit/editor-common/type-ahead';
8
+ import type { ExtractInjectionAPI, TypeAheadItem, TypeAheadHandler } from '@atlaskit/editor-common/types';
9
+ import type { DecorationSet, EditorView } from '@atlaskit/editor-prosemirror/view';
10
+ import { CloseSelectionOptions } from '../../pm-plugins/constants';
11
+ import type { TypeAheadPlugin } from '../../typeAheadPluginType';
12
+ import type { TypeAheadErrorInfo } from '../../types';
13
+ type TypeAheadPopupProps = {
14
+ triggerHandler: TypeAheadHandler;
15
+ editorView: EditorView;
16
+ anchorElement: HTMLElement;
17
+ popupsMountPoint?: HTMLElement;
18
+ popupsBoundariesElement?: HTMLElement;
19
+ popupsScrollableElement?: HTMLElement;
20
+ items: Array<TypeAheadItem>;
21
+ errorInfo: TypeAheadErrorInfo;
22
+ decorationSet: DecorationSet;
23
+ isEmptyQuery: boolean;
24
+ query: string;
25
+ onItemInsert: (mode: SelectItemMode, index: number) => void;
26
+ cancel: (params: {
27
+ setSelectionAt: CloseSelectionOptions;
28
+ addPrefixTrigger: boolean;
29
+ forceFocusOnEditor: boolean;
30
+ }) => void;
31
+ api: ExtractInjectionAPI<TypeAheadPlugin> | undefined;
32
+ };
33
+ export declare const TypeAheadPopup: React.MemoExoticComponent<(props: TypeAheadPopupProps) => jsx.JSX.Element>;
34
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-type-ahead",
3
- "version": "1.12.2",
3
+ "version": "1.13.1",
4
4
  "description": "Type-ahead plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -32,17 +32,19 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "@atlaskit/adf-schema": "^46.1.0",
35
- "@atlaskit/editor-common": "^99.9.0",
36
- "@atlaskit/editor-plugin-analytics": "^1.11.0",
37
- "@atlaskit/editor-plugin-connectivity": "^1.1.0",
38
- "@atlaskit/editor-prosemirror": "6.2.1",
35
+ "@atlaskit/editor-common": "^99.11.0",
36
+ "@atlaskit/editor-element-browser": "^0.0.1",
37
+ "@atlaskit/editor-plugin-analytics": "^1.12.0",
38
+ "@atlaskit/editor-plugin-connectivity": "^1.3.0",
39
+ "@atlaskit/editor-prosemirror": "7.0.0",
39
40
  "@atlaskit/editor-shared-styles": "^3.2.0",
40
41
  "@atlaskit/heading": "^4.1.0",
41
42
  "@atlaskit/menu": "^2.14.0",
42
43
  "@atlaskit/platform-feature-flags": "^1.0.0",
43
- "@atlaskit/primitives": "^13.4.0",
44
- "@atlaskit/prosemirror-input-rules": "^3.2.0",
44
+ "@atlaskit/primitives": "^13.5.0",
45
+ "@atlaskit/prosemirror-input-rules": "^3.3.0",
45
46
  "@atlaskit/theme": "^15.0.0",
47
+ "@atlaskit/tmp-editor-statsig": "^2.45.0",
46
48
  "@atlaskit/tokens": "^3.3.0",
47
49
  "@babel/runtime": "^7.0.0",
48
50
  "@emotion/react": "^11.7.1",