@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 +21 -0
- package/dist/cjs/ui/ContentComponent.js +22 -7
- package/dist/cjs/ui/modern/TypeAheadMenu.js +77 -0
- package/dist/cjs/ui/modern/TypeAheadPopup.js +282 -0
- package/dist/es2019/ui/ContentComponent.js +21 -6
- package/dist/es2019/ui/modern/TypeAheadMenu.js +68 -0
- package/dist/es2019/ui/modern/TypeAheadPopup.js +275 -0
- package/dist/esm/ui/ContentComponent.js +21 -6
- package/dist/esm/ui/modern/TypeAheadMenu.js +70 -0
- package/dist/esm/ui/modern/TypeAheadPopup.js +271 -0
- package/dist/types/ui/modern/TypeAheadMenu.d.ts +13 -0
- package/dist/types/ui/modern/TypeAheadPopup.d.ts +34 -0
- package/dist/types-ts4.5/ui/modern/TypeAheadMenu.d.ts +13 -0
- package/dist/types-ts4.5/ui/modern/TypeAheadPopup.d.ts +34 -0
- package/package.json +9 -7
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
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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.
|
|
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.
|
|
36
|
-
"@atlaskit/editor-
|
|
37
|
-
"@atlaskit/editor-plugin-
|
|
38
|
-
"@atlaskit/editor-
|
|
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.
|
|
44
|
-
"@atlaskit/prosemirror-input-rules": "^3.
|
|
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",
|