@atlaskit/editor-plugin-type-ahead 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +11 -0
- package/CHANGELOG.md +12 -0
- package/dist/cjs/api.js +215 -0
- package/dist/cjs/commands/insert-type-ahead-item.js +205 -0
- package/dist/cjs/commands/update-list-items.js +23 -0
- package/dist/cjs/commands/update-query.js +27 -0
- package/dist/cjs/commands/update-selected-index.js +27 -0
- package/dist/cjs/constants.js +15 -0
- package/dist/cjs/index.js +8 -1
- package/dist/cjs/insert-utils.js +107 -0
- package/dist/cjs/messages.js +79 -0
- package/dist/cjs/plugin.js +382 -0
- package/dist/cjs/pm-plugins/actions.js +16 -0
- package/dist/cjs/pm-plugins/decorations.js +148 -0
- package/dist/cjs/pm-plugins/input-rules.js +36 -0
- package/dist/cjs/pm-plugins/insert-item-plugin.js +22 -0
- package/dist/cjs/pm-plugins/key.js +8 -0
- package/dist/cjs/pm-plugins/main.js +110 -0
- package/dist/cjs/pm-plugins/reducer.js +158 -0
- package/dist/cjs/pm-plugins/utils.js +18 -0
- package/dist/cjs/stats-modifier.js +42 -0
- package/dist/cjs/transforms/close-type-ahead.js +13 -0
- package/dist/cjs/transforms/open-typeahead-at-cursor.js +75 -0
- package/dist/cjs/transforms/set-selection-before-query.js +18 -0
- package/dist/cjs/ui/AssistiveText.js +120 -0
- package/dist/cjs/ui/InputQuery.js +400 -0
- package/dist/cjs/ui/TypeAheadList.js +285 -0
- package/dist/cjs/ui/TypeAheadListItem.js +181 -0
- package/dist/cjs/ui/TypeAheadPopup.js +230 -0
- package/dist/cjs/ui/WrapperTypeAhead.js +127 -0
- package/dist/cjs/ui/hooks/use-item-insert.js +109 -0
- package/dist/cjs/ui/hooks/use-load-items.js +50 -0
- package/dist/cjs/ui/hooks/use-on-force-select.js +41 -0
- package/dist/cjs/utils.js +130 -0
- package/dist/es2019/api.js +205 -0
- package/dist/es2019/commands/insert-type-ahead-item.js +204 -0
- package/dist/es2019/commands/update-list-items.js +17 -0
- package/dist/es2019/commands/update-query.js +21 -0
- package/dist/es2019/commands/update-selected-index.js +21 -0
- package/dist/es2019/constants.js +9 -0
- package/dist/es2019/index.js +1 -1
- package/dist/es2019/insert-utils.js +106 -0
- package/dist/es2019/messages.js +73 -0
- package/dist/es2019/plugin.js +381 -0
- package/dist/es2019/pm-plugins/actions.js +10 -0
- package/dist/es2019/pm-plugins/decorations.js +148 -0
- package/dist/es2019/pm-plugins/input-rules.js +29 -0
- package/dist/es2019/pm-plugins/insert-item-plugin.js +16 -0
- package/dist/es2019/pm-plugins/key.js +2 -0
- package/dist/es2019/pm-plugins/main.js +106 -0
- package/dist/es2019/pm-plugins/reducer.js +160 -0
- package/dist/es2019/pm-plugins/utils.js +12 -0
- package/dist/es2019/stats-modifier.js +33 -0
- package/dist/es2019/transforms/close-type-ahead.js +7 -0
- package/dist/es2019/transforms/open-typeahead-at-cursor.js +71 -0
- package/dist/es2019/transforms/set-selection-before-query.js +10 -0
- package/dist/es2019/ui/AssistiveText.js +88 -0
- package/dist/es2019/ui/InputQuery.js +393 -0
- package/dist/es2019/ui/TypeAheadList.js +273 -0
- package/dist/es2019/ui/TypeAheadListItem.js +216 -0
- package/dist/es2019/ui/TypeAheadPopup.js +233 -0
- package/dist/es2019/ui/WrapperTypeAhead.js +109 -0
- package/dist/es2019/ui/hooks/use-item-insert.js +112 -0
- package/dist/es2019/ui/hooks/use-load-items.js +41 -0
- package/dist/es2019/ui/hooks/use-on-force-select.js +38 -0
- package/dist/es2019/utils.js +126 -0
- package/dist/esm/api.js +209 -0
- package/dist/esm/commands/insert-type-ahead-item.js +198 -0
- package/dist/esm/commands/update-list-items.js +17 -0
- package/dist/esm/commands/update-query.js +21 -0
- package/dist/esm/commands/update-selected-index.js +21 -0
- package/dist/esm/constants.js +9 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/insert-utils.js +101 -0
- package/dist/esm/messages.js +73 -0
- package/dist/esm/plugin.js +374 -0
- package/dist/esm/pm-plugins/actions.js +10 -0
- package/dist/esm/pm-plugins/decorations.js +141 -0
- package/dist/esm/pm-plugins/input-rules.js +29 -0
- package/dist/esm/pm-plugins/insert-item-plugin.js +16 -0
- package/dist/esm/pm-plugins/key.js +2 -0
- package/dist/esm/pm-plugins/main.js +104 -0
- package/dist/esm/pm-plugins/reducer.js +151 -0
- package/dist/esm/pm-plugins/utils.js +12 -0
- package/dist/esm/stats-modifier.js +35 -0
- package/dist/esm/transforms/close-type-ahead.js +7 -0
- package/dist/esm/transforms/open-typeahead-at-cursor.js +69 -0
- package/dist/esm/transforms/set-selection-before-query.js +12 -0
- package/dist/esm/ui/AssistiveText.js +115 -0
- package/dist/esm/ui/InputQuery.js +390 -0
- package/dist/esm/ui/TypeAheadList.js +276 -0
- package/dist/esm/ui/TypeAheadListItem.js +171 -0
- package/dist/esm/ui/TypeAheadPopup.js +220 -0
- package/dist/esm/ui/WrapperTypeAhead.js +117 -0
- package/dist/esm/ui/hooks/use-item-insert.js +103 -0
- package/dist/esm/ui/hooks/use-load-items.js +43 -0
- package/dist/esm/ui/hooks/use-on-force-select.js +35 -0
- package/dist/esm/utils.js +124 -0
- package/dist/types/api.d.ts +61 -0
- package/dist/types/commands/insert-type-ahead-item.d.ts +12 -0
- package/dist/types/commands/update-list-items.d.ts +3 -0
- package/dist/types/commands/update-query.d.ts +2 -0
- package/dist/types/commands/update-selected-index.d.ts +2 -0
- package/dist/types/constants.d.ts +8 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/insert-utils.d.ts +18 -0
- package/dist/types/messages.d.ts +72 -0
- package/dist/types/plugin.d.ts +10 -0
- package/dist/types/pm-plugins/actions.d.ts +9 -0
- package/dist/types/pm-plugins/decorations.d.ts +14 -0
- package/dist/types/pm-plugins/input-rules.d.ts +6 -0
- package/dist/types/pm-plugins/insert-item-plugin.d.ts +2 -0
- package/dist/types/pm-plugins/key.d.ts +3 -0
- package/dist/types/pm-plugins/main.d.ts +14 -0
- package/dist/types/pm-plugins/reducer.d.ts +10 -0
- package/dist/types/pm-plugins/utils.d.ts +4 -0
- package/dist/types/stats-modifier.d.ts +20 -0
- package/dist/types/transforms/close-type-ahead.d.ts +2 -0
- package/dist/types/transforms/open-typeahead-at-cursor.d.ts +11 -0
- package/dist/types/transforms/set-selection-before-query.d.ts +2 -0
- package/dist/types/types.d.ts +64 -3
- package/dist/types/ui/AssistiveText.d.ts +33 -0
- package/dist/types/ui/InputQuery.d.ts +26 -0
- package/dist/types/ui/TypeAheadList.d.ts +25 -0
- package/dist/types/ui/TypeAheadListItem.d.ts +18 -0
- package/dist/types/ui/TypeAheadPopup.d.ts +29 -0
- package/dist/types/ui/WrapperTypeAhead.d.ts +20 -0
- package/dist/types/ui/hooks/use-item-insert.d.ts +3 -0
- package/dist/types/ui/hooks/use-load-items.d.ts +3 -0
- package/dist/types/ui/hooks/use-on-force-select.d.ts +11 -0
- package/dist/types/utils.d.ts +27 -0
- package/dist/types-ts4.5/api.d.ts +61 -0
- package/dist/types-ts4.5/commands/insert-type-ahead-item.d.ts +12 -0
- package/dist/types-ts4.5/commands/update-list-items.d.ts +3 -0
- package/dist/types-ts4.5/commands/update-query.d.ts +2 -0
- package/dist/types-ts4.5/commands/update-selected-index.d.ts +2 -0
- package/dist/types-ts4.5/constants.d.ts +8 -0
- package/dist/types-ts4.5/index.d.ts +2 -1
- package/dist/types-ts4.5/insert-utils.d.ts +18 -0
- package/dist/types-ts4.5/messages.d.ts +72 -0
- package/dist/types-ts4.5/plugin.d.ts +10 -0
- package/dist/types-ts4.5/pm-plugins/actions.d.ts +9 -0
- package/dist/types-ts4.5/pm-plugins/decorations.d.ts +14 -0
- package/dist/types-ts4.5/pm-plugins/input-rules.d.ts +6 -0
- package/dist/types-ts4.5/pm-plugins/insert-item-plugin.d.ts +2 -0
- package/dist/types-ts4.5/pm-plugins/key.d.ts +3 -0
- package/dist/types-ts4.5/pm-plugins/main.d.ts +14 -0
- package/dist/types-ts4.5/pm-plugins/reducer.d.ts +10 -0
- package/dist/types-ts4.5/pm-plugins/utils.d.ts +4 -0
- package/dist/types-ts4.5/stats-modifier.d.ts +20 -0
- package/dist/types-ts4.5/transforms/close-type-ahead.d.ts +2 -0
- package/dist/types-ts4.5/transforms/open-typeahead-at-cursor.d.ts +11 -0
- package/dist/types-ts4.5/transforms/set-selection-before-query.d.ts +2 -0
- package/dist/types-ts4.5/types.d.ts +64 -3
- package/dist/types-ts4.5/ui/AssistiveText.d.ts +33 -0
- package/dist/types-ts4.5/ui/InputQuery.d.ts +26 -0
- package/dist/types-ts4.5/ui/TypeAheadList.d.ts +25 -0
- package/dist/types-ts4.5/ui/TypeAheadListItem.d.ts +18 -0
- package/dist/types-ts4.5/ui/TypeAheadPopup.d.ts +29 -0
- package/dist/types-ts4.5/ui/WrapperTypeAhead.d.ts +20 -0
- package/dist/types-ts4.5/ui/hooks/use-item-insert.d.ts +7 -0
- package/dist/types-ts4.5/ui/hooks/use-load-items.d.ts +3 -0
- package/dist/types-ts4.5/ui/hooks/use-on-force-select.d.ts +11 -0
- package/dist/types-ts4.5/utils.d.ts +27 -0
- package/package.json +21 -28
- package/report.api.md +32 -1
- package/tmp/api-report-tmp.d.ts +29 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/** @jsx jsx */
|
|
2
|
+
import React, { useCallback, useLayoutEffect, useMemo } from 'react';
|
|
3
|
+
import { css, jsx } from '@emotion/react';
|
|
4
|
+
import { useIntl } from 'react-intl-next';
|
|
5
|
+
import { IconFallback } from '@atlaskit/editor-common/quick-insert';
|
|
6
|
+
import { SelectItemMode } from '@atlaskit/editor-common/type-ahead';
|
|
7
|
+
import { relativeFontSizeToBase16 } from '@atlaskit/editor-shared-styles';
|
|
8
|
+
import { shortcutStyle } from '@atlaskit/editor-shared-styles/shortcut';
|
|
9
|
+
import { ButtonItem } from '@atlaskit/menu';
|
|
10
|
+
import { B400, DN600, N200, N30, N800 } from '@atlaskit/theme/colors';
|
|
11
|
+
import { themed } from '@atlaskit/theme/components';
|
|
12
|
+
import { borderRadius } from '@atlaskit/theme/constants';
|
|
13
|
+
import { typeAheadListMessages } from '../messages';
|
|
14
|
+
export const ICON_HEIGHT = 40;
|
|
15
|
+
export const ICON_WIDTH = 40;
|
|
16
|
+
export const ITEM_PADDING = 12;
|
|
17
|
+
export const itemIcon = css`
|
|
18
|
+
width: ${ICON_WIDTH}px;
|
|
19
|
+
height: ${ICON_HEIGHT}px;
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
border: 1px solid ${"var(--ds-border, rgba(223, 225, 229, 0.5))"}; /* N60 at 50% */
|
|
22
|
+
border-radius: ${borderRadius()}px;
|
|
23
|
+
box-sizing: border-box;
|
|
24
|
+
|
|
25
|
+
display: flex;
|
|
26
|
+
justify-content: center;
|
|
27
|
+
align-items: center;
|
|
28
|
+
|
|
29
|
+
div {
|
|
30
|
+
width: ${ICON_WIDTH}px;
|
|
31
|
+
height: ${ICON_HEIGHT}px;
|
|
32
|
+
}
|
|
33
|
+
`;
|
|
34
|
+
const itemBody = css`
|
|
35
|
+
display: flex;
|
|
36
|
+
flex-direction: row;
|
|
37
|
+
flex-wrap: nowrap;
|
|
38
|
+
justify-content: space-between;
|
|
39
|
+
`;
|
|
40
|
+
const itemText = theme => css`
|
|
41
|
+
white-space: initial;
|
|
42
|
+
color: ${themed({
|
|
43
|
+
light: `var(--ds-text, ${N800})`,
|
|
44
|
+
dark: `var(--ds-text, ${DN600})`
|
|
45
|
+
})(theme)};
|
|
46
|
+
.item-title {
|
|
47
|
+
line-height: 1.4;
|
|
48
|
+
}
|
|
49
|
+
.item-description {
|
|
50
|
+
font-size: ${relativeFontSizeToBase16(12)};
|
|
51
|
+
color: ${`var(--ds-text-subtlest, ${N200})`};
|
|
52
|
+
margin-top: 3px;
|
|
53
|
+
}
|
|
54
|
+
`;
|
|
55
|
+
const itemAfter = css`
|
|
56
|
+
flex: 0 0 auto;
|
|
57
|
+
`;
|
|
58
|
+
const customRenderItemDivStyle = css`
|
|
59
|
+
overflow: hidden;
|
|
60
|
+
&:focus {
|
|
61
|
+
box-shadow: inset 2px 0px 0px ${`var(--ds-border-focused, ${B400})`};
|
|
62
|
+
background-color: ${`var(--ds-background-neutral-subtle-hovered, ${N30})`};
|
|
63
|
+
outline: none;
|
|
64
|
+
}
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* This CSS emulates the desired behaviour with :focus-visible for firefox.
|
|
69
|
+
* Firefox unfortunately does not register keyboard focus if user mouseDown and drag a typeahead item
|
|
70
|
+
* resulting in focus-visible style not drawn.
|
|
71
|
+
*/
|
|
72
|
+
const selectionFrame = {
|
|
73
|
+
'& > button:focus': {
|
|
74
|
+
boxShadow: `inset 2px 0px 0px ${`var(--ds-border-focused, ${B400})`};`,
|
|
75
|
+
backgroundColor: `${`var(--ds-background-neutral-subtle-hovered, ${N30})`}`,
|
|
76
|
+
outline: 'none',
|
|
77
|
+
'&:active': {
|
|
78
|
+
boxShadow: 'none'
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
'& > button:hover': {
|
|
82
|
+
backgroundColor: 'inherit',
|
|
83
|
+
outline: 'none'
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const selectedStyle = css`
|
|
87
|
+
background-color: ${`var(--ds-background-neutral-subtle-hovered, ${N30})`};
|
|
88
|
+
box-shadow: inset 2px 0px 0px ${`var(--ds-border-focused, ${B400})`};
|
|
89
|
+
`;
|
|
90
|
+
const FallbackIcon = /*#__PURE__*/React.memo(({
|
|
91
|
+
label
|
|
92
|
+
}) => {
|
|
93
|
+
return jsx(IconFallback, null);
|
|
94
|
+
});
|
|
95
|
+
const noop = () => {};
|
|
96
|
+
const AssistiveText = ({
|
|
97
|
+
title,
|
|
98
|
+
description,
|
|
99
|
+
shortcut
|
|
100
|
+
}) => {
|
|
101
|
+
const intl = useIntl();
|
|
102
|
+
const descriptionText = description ? ` ${intl.formatMessage(typeAheadListMessages.descriptionLabel)} ${description}.` : '';
|
|
103
|
+
const shortcutText = shortcut ? ` ${intl.formatMessage(typeAheadListMessages.shortcutLabel)} ${shortcut}.` : '';
|
|
104
|
+
return jsx("span", {
|
|
105
|
+
className: "assistive"
|
|
106
|
+
}, `${title}. ${descriptionText} ${shortcutText}`);
|
|
107
|
+
};
|
|
108
|
+
export const TypeAheadListItem = ({
|
|
109
|
+
item,
|
|
110
|
+
itemsLength,
|
|
111
|
+
selectedIndex,
|
|
112
|
+
onItemClick,
|
|
113
|
+
itemIndex,
|
|
114
|
+
ariaLabel
|
|
115
|
+
}) => {
|
|
116
|
+
/**
|
|
117
|
+
* To select and highlight the first Item when no item is selected
|
|
118
|
+
* However selectedIndex remains -1, So that user does not skip the first item when down arrow key is used from typeahead query(inputQuery.tsx)
|
|
119
|
+
*/
|
|
120
|
+
const isSelected = itemIndex === selectedIndex || selectedIndex === -1 && itemIndex === 0;
|
|
121
|
+
const {
|
|
122
|
+
icon,
|
|
123
|
+
title,
|
|
124
|
+
render: customRenderItem
|
|
125
|
+
} = item;
|
|
126
|
+
const elementIcon = useMemo(() => {
|
|
127
|
+
return jsx("div", {
|
|
128
|
+
css: itemIcon
|
|
129
|
+
}, icon ? icon() : jsx(FallbackIcon, {
|
|
130
|
+
label: title
|
|
131
|
+
}));
|
|
132
|
+
}, [icon, title]);
|
|
133
|
+
const insertSelectedItem = useCallback(() => {
|
|
134
|
+
onItemClick(SelectItemMode.SELECTED, itemIndex);
|
|
135
|
+
}, [onItemClick, itemIndex]);
|
|
136
|
+
const customItemRef = /*#__PURE__*/React.createRef();
|
|
137
|
+
const buttonItemRef = /*#__PURE__*/React.createRef();
|
|
138
|
+
const shouldUpdateFocus = selectedIndex === itemIndex;
|
|
139
|
+
useLayoutEffect(() => {
|
|
140
|
+
if (shouldUpdateFocus) {
|
|
141
|
+
var _customItemRef$curren;
|
|
142
|
+
customItemRef === null || customItemRef === void 0 ? void 0 : (_customItemRef$curren = customItemRef.current) === null || _customItemRef$curren === void 0 ? void 0 : _customItemRef$curren.focus();
|
|
143
|
+
}
|
|
144
|
+
}, [customItemRef, shouldUpdateFocus]);
|
|
145
|
+
useLayoutEffect(() => {
|
|
146
|
+
if (shouldUpdateFocus) {
|
|
147
|
+
var _buttonItemRef$curren;
|
|
148
|
+
buttonItemRef === null || buttonItemRef === void 0 ? void 0 : (_buttonItemRef$curren = buttonItemRef.current) === null || _buttonItemRef$curren === void 0 ? void 0 : _buttonItemRef$curren.focus();
|
|
149
|
+
}
|
|
150
|
+
}, [buttonItemRef, shouldUpdateFocus]);
|
|
151
|
+
const customItem = useMemo(() => {
|
|
152
|
+
if (!customRenderItem) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
const Comp = customRenderItem;
|
|
156
|
+
const listItemClasses = [customRenderItemDivStyle, isSelected && selectedStyle];
|
|
157
|
+
return jsx("div", {
|
|
158
|
+
"aria-selected": isSelected,
|
|
159
|
+
role: "option",
|
|
160
|
+
"aria-label": ariaLabel,
|
|
161
|
+
"aria-setsize": itemsLength,
|
|
162
|
+
"aria-posinset": itemIndex,
|
|
163
|
+
tabIndex: 0,
|
|
164
|
+
css: listItemClasses,
|
|
165
|
+
className: `ak-typeahead-item ${isSelected ? 'typeahead-selected-item' : ''}`
|
|
166
|
+
//CSS classes added for test cases purpose
|
|
167
|
+
,
|
|
168
|
+
ref: customItemRef
|
|
169
|
+
}, jsx("div", {
|
|
170
|
+
"aria-hidden": true
|
|
171
|
+
}, jsx(Comp, {
|
|
172
|
+
onClick: insertSelectedItem,
|
|
173
|
+
isSelected: false //The selection styles are handled in the parent div instead. Hence isSelected is made false always.
|
|
174
|
+
,
|
|
175
|
+
onHover: noop
|
|
176
|
+
})));
|
|
177
|
+
}, [customRenderItem, isSelected, ariaLabel, itemsLength, customItemRef, insertSelectedItem, itemIndex]);
|
|
178
|
+
if (customItem) {
|
|
179
|
+
return customItem;
|
|
180
|
+
}
|
|
181
|
+
const listItemClasses = [selectionFrame, isSelected && selectedStyle];
|
|
182
|
+
return jsx("span", {
|
|
183
|
+
css: listItemClasses
|
|
184
|
+
}, jsx(ButtonItem, {
|
|
185
|
+
onClick: insertSelectedItem,
|
|
186
|
+
iconBefore: elementIcon,
|
|
187
|
+
isSelected: isSelected,
|
|
188
|
+
"aria-selected": isSelected,
|
|
189
|
+
"aria-label": title,
|
|
190
|
+
"aria-setsize": itemsLength,
|
|
191
|
+
"aria-posinset": itemIndex,
|
|
192
|
+
role: "option",
|
|
193
|
+
ref: buttonItemRef
|
|
194
|
+
// @ts-ignore
|
|
195
|
+
,
|
|
196
|
+
css: listItemClasses
|
|
197
|
+
}, jsx("div", {
|
|
198
|
+
"aria-hidden": true
|
|
199
|
+
}, jsx("div", {
|
|
200
|
+
css: itemText
|
|
201
|
+
}, jsx("div", {
|
|
202
|
+
css: itemBody
|
|
203
|
+
}, jsx("div", {
|
|
204
|
+
className: "item-title"
|
|
205
|
+
}, item.title), jsx("div", {
|
|
206
|
+
css: itemAfter
|
|
207
|
+
}, item.keyshortcut && jsx("div", {
|
|
208
|
+
css: shortcutStyle
|
|
209
|
+
}, item.keyshortcut))), jsx("div", {
|
|
210
|
+
className: "item-description"
|
|
211
|
+
}, item.description))), jsx(AssistiveText, {
|
|
212
|
+
title: item.title,
|
|
213
|
+
description: item.description,
|
|
214
|
+
shortcut: item.keyshortcut
|
|
215
|
+
})));
|
|
216
|
+
};
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/** @jsx jsx */
|
|
2
|
+
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { css, jsx } from '@emotion/react';
|
|
4
|
+
import rafSchedule from 'raf-schd';
|
|
5
|
+
import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
|
|
6
|
+
import { findOverflowScrollParent, Popup } from '@atlaskit/editor-common/ui';
|
|
7
|
+
import { akEditorFloatingDialogZIndex } from '@atlaskit/editor-shared-styles';
|
|
8
|
+
import { N0, N50A, N60A } from '@atlaskit/theme/colors';
|
|
9
|
+
import { borderRadius } from '@atlaskit/theme/constants';
|
|
10
|
+
import { CloseSelectionOptions, TYPE_AHEAD_DECORATION_DATA_ATTRIBUTE, TYPE_AHEAD_POPUP_CONTENT_CLASS } from '../constants';
|
|
11
|
+
import { TypeAheadList } from './TypeAheadList';
|
|
12
|
+
import { ITEM_PADDING } from './TypeAheadListItem';
|
|
13
|
+
const DEFAULT_TYPEAHEAD_MENU_HEIGHT = 380;
|
|
14
|
+
const typeAheadContent = css`
|
|
15
|
+
background: ${`var(--ds-surface-overlay, ${N0})`};
|
|
16
|
+
border-radius: ${borderRadius()}px;
|
|
17
|
+
box-shadow: ${`var(--ds-shadow-overlay, ${`0 0 1px ${N60A}, 0 4px 8px -2px ${N50A}`})`};
|
|
18
|
+
padding: ${"var(--ds-space-050, 4px)"} 0;
|
|
19
|
+
width: 320px;
|
|
20
|
+
max-height: 380px; /* ~5.5 visibile items */
|
|
21
|
+
overflow-y: auto;
|
|
22
|
+
-ms-overflow-style: -ms-autohiding-scrollbar;
|
|
23
|
+
position: relative;
|
|
24
|
+
`;
|
|
25
|
+
const Highlight = ({
|
|
26
|
+
state,
|
|
27
|
+
triggerHandler
|
|
28
|
+
}) => {
|
|
29
|
+
if (!(triggerHandler !== null && triggerHandler !== void 0 && triggerHandler.getHighlight)) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return triggerHandler.getHighlight(state);
|
|
33
|
+
};
|
|
34
|
+
const OFFSET = [0, 8];
|
|
35
|
+
export const TypeAheadPopup = /*#__PURE__*/React.memo(props => {
|
|
36
|
+
const {
|
|
37
|
+
editorView,
|
|
38
|
+
triggerHandler,
|
|
39
|
+
anchorElement,
|
|
40
|
+
popupsMountPoint,
|
|
41
|
+
popupsBoundariesElement,
|
|
42
|
+
popupsScrollableElement,
|
|
43
|
+
items,
|
|
44
|
+
selectedIndex,
|
|
45
|
+
onItemInsert,
|
|
46
|
+
fireAnalyticsCallback,
|
|
47
|
+
isEmptyQuery,
|
|
48
|
+
cancel
|
|
49
|
+
} = props;
|
|
50
|
+
const ref = useRef(null);
|
|
51
|
+
const startTime = useMemo(() => performance.now(),
|
|
52
|
+
// In case those props changes
|
|
53
|
+
// we need to recreate the startTime
|
|
54
|
+
[items, isEmptyQuery, fireAnalyticsCallback, triggerHandler] // eslint-disable-line react-hooks/exhaustive-deps
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!fireAnalyticsCallback) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const stopTime = performance.now();
|
|
62
|
+
const time = stopTime - startTime;
|
|
63
|
+
fireAnalyticsCallback({
|
|
64
|
+
payload: {
|
|
65
|
+
action: ACTION.RENDERED,
|
|
66
|
+
actionSubject: ACTION_SUBJECT.TYPEAHEAD,
|
|
67
|
+
eventType: EVENT_TYPE.OPERATIONAL,
|
|
68
|
+
attributes: {
|
|
69
|
+
time,
|
|
70
|
+
items: items.length,
|
|
71
|
+
initial: isEmptyQuery
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}, [startTime, items, fireAnalyticsCallback, isEmptyQuery,
|
|
76
|
+
// In case the current triggerHandler changes
|
|
77
|
+
// e.g: Inserting a mention using the quick insert
|
|
78
|
+
// we need to send the event again
|
|
79
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
80
|
+
triggerHandler]);
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (!fireAnalyticsCallback) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
fireAnalyticsCallback({
|
|
86
|
+
payload: {
|
|
87
|
+
action: ACTION.VIEWED,
|
|
88
|
+
actionSubject: ACTION_SUBJECT.TYPEAHEAD_ITEM,
|
|
89
|
+
eventType: EVENT_TYPE.OPERATIONAL,
|
|
90
|
+
attributes: {
|
|
91
|
+
index: selectedIndex,
|
|
92
|
+
items: items.length
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}, [items, fireAnalyticsCallback, selectedIndex,
|
|
97
|
+
// In case the current triggerHandler changes
|
|
98
|
+
// e.g: Inserting a mention using the quick insert
|
|
99
|
+
// we need to send the event again
|
|
100
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
101
|
+
triggerHandler]);
|
|
102
|
+
const [fitHeight, setFitHeight] = useState(DEFAULT_TYPEAHEAD_MENU_HEIGHT);
|
|
103
|
+
const getFitHeight = useCallback(() => {
|
|
104
|
+
if (!anchorElement || !popupsMountPoint) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const target = anchorElement;
|
|
108
|
+
const {
|
|
109
|
+
top: targetTop,
|
|
110
|
+
height: targetHeight
|
|
111
|
+
} = target.getBoundingClientRect();
|
|
112
|
+
const boundariesElement = document.body;
|
|
113
|
+
const {
|
|
114
|
+
height: boundariesHeight,
|
|
115
|
+
top: boundariesTop
|
|
116
|
+
} = boundariesElement.getBoundingClientRect();
|
|
117
|
+
|
|
118
|
+
// Calculating the space above and space below our decoration
|
|
119
|
+
const spaceAbove = targetTop - (boundariesTop - boundariesElement.scrollTop);
|
|
120
|
+
const spaceBelow = boundariesTop + boundariesHeight - (targetTop + targetHeight);
|
|
121
|
+
|
|
122
|
+
// Keep default height if more than enough space
|
|
123
|
+
if (spaceBelow >= DEFAULT_TYPEAHEAD_MENU_HEIGHT) {
|
|
124
|
+
return setFitHeight(DEFAULT_TYPEAHEAD_MENU_HEIGHT);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Determines whether typeahead will fit above or below decoration
|
|
128
|
+
// and return the space available.
|
|
129
|
+
const newFitHeight = spaceBelow >= spaceAbove ? spaceBelow : spaceAbove;
|
|
130
|
+
|
|
131
|
+
// Each typeahead item has some padding
|
|
132
|
+
// We want to leave some space at the top so first item
|
|
133
|
+
// is not partially cropped
|
|
134
|
+
const fitHeightWithSpace = newFitHeight - ITEM_PADDING * 2;
|
|
135
|
+
|
|
136
|
+
// Ensure typeahead height is max its default height
|
|
137
|
+
const minFitHeight = Math.min(DEFAULT_TYPEAHEAD_MENU_HEIGHT, fitHeightWithSpace);
|
|
138
|
+
return setFitHeight(minFitHeight);
|
|
139
|
+
}, [anchorElement, popupsMountPoint]);
|
|
140
|
+
const getFitHeightDebounced = rafSchedule(getFitHeight);
|
|
141
|
+
useLayoutEffect(() => {
|
|
142
|
+
const scrollableElement = popupsScrollableElement || findOverflowScrollParent(anchorElement);
|
|
143
|
+
getFitHeight();
|
|
144
|
+
window.addEventListener('resize', getFitHeightDebounced);
|
|
145
|
+
if (scrollableElement) {
|
|
146
|
+
scrollableElement.addEventListener('scroll', getFitHeightDebounced);
|
|
147
|
+
}
|
|
148
|
+
return () => {
|
|
149
|
+
window.removeEventListener('resize', getFitHeightDebounced);
|
|
150
|
+
if (scrollableElement) {
|
|
151
|
+
scrollableElement.removeEventListener('scroll', getFitHeightDebounced);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}, [anchorElement, popupsScrollableElement, getFitHeightDebounced, getFitHeight]);
|
|
155
|
+
useLayoutEffect(() => {
|
|
156
|
+
const focusOut = event => {
|
|
157
|
+
var _window$getSelection;
|
|
158
|
+
const {
|
|
159
|
+
relatedTarget
|
|
160
|
+
} = event;
|
|
161
|
+
|
|
162
|
+
// Given the user is changing the focus
|
|
163
|
+
// When the target is inside the TypeAhead Popup
|
|
164
|
+
// Then the popup should stay open
|
|
165
|
+
if (relatedTarget instanceof HTMLElement && relatedTarget.closest && (relatedTarget.closest(`.${TYPE_AHEAD_POPUP_CONTENT_CLASS}`) || relatedTarget.closest(`[data-type-ahead="${TYPE_AHEAD_DECORATION_DATA_ATTRIBUTE}"]`))) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (!(((_window$getSelection = window.getSelection()) === null || _window$getSelection === void 0 ? void 0 : _window$getSelection.type) === 'Range')) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
cancel({
|
|
172
|
+
addPrefixTrigger: true,
|
|
173
|
+
setSelectionAt: CloseSelectionOptions.AFTER_TEXT_INSERTED,
|
|
174
|
+
forceFocusOnEditor: false
|
|
175
|
+
});
|
|
176
|
+
};
|
|
177
|
+
const {
|
|
178
|
+
current: element
|
|
179
|
+
} = ref;
|
|
180
|
+
element === null || element === void 0 ? void 0 : element.addEventListener('focusout', focusOut);
|
|
181
|
+
return () => {
|
|
182
|
+
element === null || element === void 0 ? void 0 : element.removeEventListener('focusout', focusOut);
|
|
183
|
+
};
|
|
184
|
+
}, [ref, cancel]);
|
|
185
|
+
|
|
186
|
+
// ED-17443 When you press escape on typeahead panel, it should remove focus and close the panel
|
|
187
|
+
// This is the expected keyboard behaviour advised by the Accessibility team
|
|
188
|
+
useLayoutEffect(() => {
|
|
189
|
+
const escape = event => {
|
|
190
|
+
if (event.key === 'Escape') {
|
|
191
|
+
cancel({
|
|
192
|
+
addPrefixTrigger: true,
|
|
193
|
+
setSelectionAt: CloseSelectionOptions.AFTER_TEXT_INSERTED,
|
|
194
|
+
forceFocusOnEditor: true
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
const {
|
|
199
|
+
current: element
|
|
200
|
+
} = ref;
|
|
201
|
+
element === null || element === void 0 ? void 0 : element.addEventListener('keydown', escape);
|
|
202
|
+
return () => {
|
|
203
|
+
element === null || element === void 0 ? void 0 : element.removeEventListener('keydown', escape);
|
|
204
|
+
};
|
|
205
|
+
}, [ref, cancel]);
|
|
206
|
+
return jsx(Popup, {
|
|
207
|
+
zIndex: akEditorFloatingDialogZIndex,
|
|
208
|
+
target: anchorElement,
|
|
209
|
+
mountTo: popupsMountPoint,
|
|
210
|
+
boundariesElement: popupsBoundariesElement,
|
|
211
|
+
scrollableElement: popupsScrollableElement,
|
|
212
|
+
fitHeight: fitHeight,
|
|
213
|
+
fitWidth: 340,
|
|
214
|
+
offset: OFFSET
|
|
215
|
+
}, jsx("div", {
|
|
216
|
+
css: typeAheadContent,
|
|
217
|
+
tabIndex: 0,
|
|
218
|
+
className: TYPE_AHEAD_POPUP_CONTENT_CLASS,
|
|
219
|
+
ref: ref
|
|
220
|
+
}, jsx(Highlight, {
|
|
221
|
+
state: editorView.state,
|
|
222
|
+
triggerHandler: triggerHandler
|
|
223
|
+
}), jsx(TypeAheadList, {
|
|
224
|
+
items: items,
|
|
225
|
+
selectedIndex: selectedIndex,
|
|
226
|
+
onItemClick: onItemInsert,
|
|
227
|
+
fitHeight: fitHeight,
|
|
228
|
+
editorView: editorView,
|
|
229
|
+
decorationElement: anchorElement,
|
|
230
|
+
triggerHandler: triggerHandler
|
|
231
|
+
})));
|
|
232
|
+
});
|
|
233
|
+
TypeAheadPopup.displayName = 'TypeAheadPopup';
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { SelectItemMode } from '@atlaskit/editor-common/type-ahead';
|
|
3
|
+
import { updateQuery } from '../commands/update-query';
|
|
4
|
+
import { getPluginState, moveSelectedIndex } from '../utils';
|
|
5
|
+
import { useItemInsert } from './hooks/use-item-insert';
|
|
6
|
+
import { useLoadItems } from './hooks/use-load-items';
|
|
7
|
+
import { useOnForceSelect } from './hooks/use-on-force-select';
|
|
8
|
+
import { InputQuery } from './InputQuery';
|
|
9
|
+
export const WrapperTypeAhead = /*#__PURE__*/React.memo(({
|
|
10
|
+
triggerHandler,
|
|
11
|
+
editorView,
|
|
12
|
+
anchorElement,
|
|
13
|
+
shouldFocusCursorInsideQuery,
|
|
14
|
+
popupsMountPoint,
|
|
15
|
+
popupsBoundariesElement,
|
|
16
|
+
popupsScrollableElement,
|
|
17
|
+
createAnalyticsEvent,
|
|
18
|
+
inputMethod,
|
|
19
|
+
getDecorationPosition,
|
|
20
|
+
reopenQuery,
|
|
21
|
+
onUndoRedo
|
|
22
|
+
}) => {
|
|
23
|
+
const [closed, setClosed] = useState(false);
|
|
24
|
+
const [query, setQuery] = useState(reopenQuery || '');
|
|
25
|
+
const queryRef = useRef(query);
|
|
26
|
+
const editorViewRef = useRef(editorView);
|
|
27
|
+
const items = useLoadItems(triggerHandler, editorView, query);
|
|
28
|
+
useLayoutEffect(() => {
|
|
29
|
+
queryRef.current = query;
|
|
30
|
+
}, [query]);
|
|
31
|
+
const [onItemInsert, onTextInsert] = useItemInsert(triggerHandler, editorView, items);
|
|
32
|
+
const selectNextItem = useMemo(() => moveSelectedIndex({
|
|
33
|
+
editorView,
|
|
34
|
+
direction: 'next'
|
|
35
|
+
}), [editorView]);
|
|
36
|
+
const selectPreviousItem = useMemo(() => moveSelectedIndex({
|
|
37
|
+
editorView,
|
|
38
|
+
direction: 'previous'
|
|
39
|
+
}), [editorView]);
|
|
40
|
+
const cancel = useCallback(({
|
|
41
|
+
setSelectionAt,
|
|
42
|
+
addPrefixTrigger,
|
|
43
|
+
text,
|
|
44
|
+
forceFocusOnEditor
|
|
45
|
+
}) => {
|
|
46
|
+
setClosed(true);
|
|
47
|
+
const fullquery = addPrefixTrigger ? `${triggerHandler.trigger}${text}` : text;
|
|
48
|
+
onTextInsert({
|
|
49
|
+
forceFocusOnEditor,
|
|
50
|
+
setSelectionAt,
|
|
51
|
+
text: fullquery
|
|
52
|
+
});
|
|
53
|
+
}, [triggerHandler, onTextInsert]);
|
|
54
|
+
const insertSelectedItem = useCallback((mode = SelectItemMode.SELECTED) => {
|
|
55
|
+
const {
|
|
56
|
+
current: view
|
|
57
|
+
} = editorViewRef;
|
|
58
|
+
const {
|
|
59
|
+
selectedIndex
|
|
60
|
+
} = getPluginState(view.state);
|
|
61
|
+
setClosed(true);
|
|
62
|
+
queueMicrotask(() => {
|
|
63
|
+
onItemInsert({
|
|
64
|
+
mode,
|
|
65
|
+
index: selectedIndex,
|
|
66
|
+
query: queryRef.current
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
}, [onItemInsert]);
|
|
70
|
+
const showTypeAheadPopupList = useCallback(() => {}, []);
|
|
71
|
+
const closePopup = useCallback(() => {
|
|
72
|
+
setClosed(true);
|
|
73
|
+
}, []);
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
const {
|
|
76
|
+
current: view
|
|
77
|
+
} = editorViewRef;
|
|
78
|
+
const pluginState = getPluginState(view.state);
|
|
79
|
+
if (query.length === 0 || query === (pluginState === null || pluginState === void 0 ? void 0 : pluginState.query) || !(pluginState !== null && pluginState !== void 0 && pluginState.triggerHandler)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
updateQuery(query)(view.state, view.dispatch);
|
|
83
|
+
}, [query, reopenQuery]);
|
|
84
|
+
useOnForceSelect({
|
|
85
|
+
triggerHandler,
|
|
86
|
+
items,
|
|
87
|
+
query,
|
|
88
|
+
editorView,
|
|
89
|
+
closePopup
|
|
90
|
+
});
|
|
91
|
+
if (closed) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
return /*#__PURE__*/React.createElement(InputQuery, {
|
|
95
|
+
triggerQueryPrefix: triggerHandler.trigger,
|
|
96
|
+
onQueryChange: setQuery,
|
|
97
|
+
onItemSelect: insertSelectedItem,
|
|
98
|
+
selectNextItem: selectNextItem,
|
|
99
|
+
selectPreviousItem: selectPreviousItem,
|
|
100
|
+
onQueryFocus: showTypeAheadPopupList,
|
|
101
|
+
cancel: cancel,
|
|
102
|
+
forceFocus: shouldFocusCursorInsideQuery,
|
|
103
|
+
onUndoRedo: onUndoRedo,
|
|
104
|
+
reopenQuery: reopenQuery,
|
|
105
|
+
editorView: editorView,
|
|
106
|
+
items: items
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
WrapperTypeAhead.displayName = 'WrapperTypeAhead';
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { useCallback, useLayoutEffect, useRef } from 'react';
|
|
2
|
+
import { SelectItemMode } from '@atlaskit/editor-common/type-ahead';
|
|
3
|
+
import { insertTypeAheadItem } from '../../commands/insert-type-ahead-item';
|
|
4
|
+
import { CloseSelectionOptions } from '../../constants';
|
|
5
|
+
import { closeTypeAhead } from '../../transforms/close-type-ahead';
|
|
6
|
+
import { setSelectionBeforeQuery } from '../../transforms/set-selection-before-query';
|
|
7
|
+
const insertRawQuery = ({
|
|
8
|
+
view,
|
|
9
|
+
setSelectionAt,
|
|
10
|
+
text,
|
|
11
|
+
forceFocusOnEditor
|
|
12
|
+
}) => {
|
|
13
|
+
const {
|
|
14
|
+
tr,
|
|
15
|
+
schema
|
|
16
|
+
} = view.state;
|
|
17
|
+
closeTypeAhead(tr);
|
|
18
|
+
if (text.length > 0) {
|
|
19
|
+
tr.replaceSelectionWith(schema.text(text));
|
|
20
|
+
if (setSelectionAt === CloseSelectionOptions.BEFORE_TEXT_INSERTED) {
|
|
21
|
+
setSelectionBeforeQuery(text)(tr);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
view.dispatch(tr);
|
|
25
|
+
if (forceFocusOnEditor) {
|
|
26
|
+
view.focus();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
export const useItemInsert = (triggerHandler, editorView, items) => {
|
|
30
|
+
const editorViewRef = useRef(editorView);
|
|
31
|
+
const itemsRef = useRef(items);
|
|
32
|
+
const onTextInsert = useCallback(({
|
|
33
|
+
setSelectionAt,
|
|
34
|
+
text,
|
|
35
|
+
forceFocusOnEditor
|
|
36
|
+
}) => {
|
|
37
|
+
if (!triggerHandler) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const {
|
|
41
|
+
current: view
|
|
42
|
+
} = editorViewRef;
|
|
43
|
+
insertRawQuery({
|
|
44
|
+
view,
|
|
45
|
+
setSelectionAt,
|
|
46
|
+
text,
|
|
47
|
+
forceFocusOnEditor
|
|
48
|
+
});
|
|
49
|
+
}, [triggerHandler]);
|
|
50
|
+
const onItemInsert = useCallback(({
|
|
51
|
+
mode,
|
|
52
|
+
index,
|
|
53
|
+
query
|
|
54
|
+
}) => {
|
|
55
|
+
const sourceListItem = itemsRef.current;
|
|
56
|
+
if (sourceListItem.length === 0 || !triggerHandler) {
|
|
57
|
+
const text = `${triggerHandler.trigger}${query}`;
|
|
58
|
+
onTextInsert({
|
|
59
|
+
forceFocusOnEditor: true,
|
|
60
|
+
setSelectionAt: CloseSelectionOptions.AFTER_TEXT_INSERTED,
|
|
61
|
+
text
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const itemToInsert = sourceListItem[index];
|
|
66
|
+
if (!itemToInsert) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const {
|
|
70
|
+
current: view
|
|
71
|
+
} = editorViewRef;
|
|
72
|
+
insertTypeAheadItem(view)({
|
|
73
|
+
item: itemToInsert,
|
|
74
|
+
handler: triggerHandler,
|
|
75
|
+
mode,
|
|
76
|
+
query,
|
|
77
|
+
sourceListItem
|
|
78
|
+
});
|
|
79
|
+
}, [triggerHandler, onTextInsert]);
|
|
80
|
+
const onItemMatch = useCallback(({
|
|
81
|
+
mode,
|
|
82
|
+
query
|
|
83
|
+
}) => {
|
|
84
|
+
const _items = itemsRef.current;
|
|
85
|
+
if (_items && _items.length > 1) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
if (_items.length === 1) {
|
|
89
|
+
queueMicrotask(() => {
|
|
90
|
+
onItemInsert({
|
|
91
|
+
mode,
|
|
92
|
+
query,
|
|
93
|
+
index: 0
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
} else {
|
|
97
|
+
const text = `${triggerHandler.trigger}${query}`;
|
|
98
|
+
queueMicrotask(() => {
|
|
99
|
+
onTextInsert({
|
|
100
|
+
forceFocusOnEditor: true,
|
|
101
|
+
setSelectionAt: CloseSelectionOptions.AFTER_TEXT_INSERTED,
|
|
102
|
+
text: mode === SelectItemMode.SPACE ? text.concat(' ') : text
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return true;
|
|
107
|
+
}, [onItemInsert, triggerHandler, onTextInsert]);
|
|
108
|
+
useLayoutEffect(() => {
|
|
109
|
+
itemsRef.current = items;
|
|
110
|
+
}, [items]);
|
|
111
|
+
return [onItemInsert, onTextInsert, onItemMatch];
|
|
112
|
+
};
|