@atlaskit/editor-plugin-quick-insert 15.1.3 → 16.0.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/CHANGELOG.md +26 -0
- package/dist/cjs/entry-points/registry-element-browser.js +12 -0
- package/dist/cjs/quickInsertPlugin.js +10 -3
- package/dist/cjs/ui/RegistryElementBrowser/RegistryElementBrowser.compiled.css +2 -0
- package/dist/cjs/ui/RegistryElementBrowser/RegistryElementBrowser.js +176 -0
- package/dist/cjs/ui/RegistryElementBrowser/RegistryElementBrowserCategories.compiled.css +2 -0
- package/dist/cjs/ui/RegistryElementBrowser/RegistryElementBrowserCategories.js +52 -0
- package/dist/cjs/ui/RegistryElementBrowser/RegistryElementBrowserFooter.js +40 -0
- package/dist/cjs/ui/RegistryElementBrowser/RegistryElementBrowserSearchResults.compiled.css +2 -0
- package/dist/cjs/ui/RegistryElementBrowser/RegistryElementBrowserSearchResults.js +123 -0
- package/dist/cjs/ui/RegistryElementBrowser/index.js +144 -0
- package/dist/cjs/ui/RegistryElementBrowser/model.js +129 -0
- package/dist/es2019/entry-points/registry-element-browser.js +2 -0
- package/dist/es2019/quickInsertPlugin.js +8 -3
- package/dist/es2019/ui/RegistryElementBrowser/RegistryElementBrowser.compiled.css +2 -0
- package/dist/es2019/ui/RegistryElementBrowser/RegistryElementBrowser.js +147 -0
- package/dist/es2019/ui/RegistryElementBrowser/RegistryElementBrowserCategories.compiled.css +2 -0
- package/dist/es2019/ui/RegistryElementBrowser/RegistryElementBrowserCategories.js +55 -0
- package/dist/es2019/ui/RegistryElementBrowser/RegistryElementBrowserFooter.js +35 -0
- package/dist/es2019/ui/RegistryElementBrowser/RegistryElementBrowserSearchResults.compiled.css +2 -0
- package/dist/es2019/ui/RegistryElementBrowser/RegistryElementBrowserSearchResults.js +112 -0
- package/dist/es2019/ui/RegistryElementBrowser/index.js +123 -0
- package/dist/es2019/ui/RegistryElementBrowser/model.js +97 -0
- package/dist/esm/entry-points/registry-element-browser.js +2 -0
- package/dist/esm/quickInsertPlugin.js +10 -3
- package/dist/esm/ui/RegistryElementBrowser/RegistryElementBrowser.compiled.css +2 -0
- package/dist/esm/ui/RegistryElementBrowser/RegistryElementBrowser.js +167 -0
- package/dist/esm/ui/RegistryElementBrowser/RegistryElementBrowserCategories.compiled.css +2 -0
- package/dist/esm/ui/RegistryElementBrowser/RegistryElementBrowserCategories.js +45 -0
- package/dist/esm/ui/RegistryElementBrowser/RegistryElementBrowserFooter.js +33 -0
- package/dist/esm/ui/RegistryElementBrowser/RegistryElementBrowserSearchResults.compiled.css +2 -0
- package/dist/esm/ui/RegistryElementBrowser/RegistryElementBrowserSearchResults.js +114 -0
- package/dist/esm/ui/RegistryElementBrowser/index.js +135 -0
- package/dist/esm/ui/RegistryElementBrowser/model.js +122 -0
- package/dist/types/entry-points/registry-element-browser.d.ts +1 -0
- package/dist/types/ui/RegistryElementBrowser/RegistryElementBrowser.d.ts +25 -0
- package/dist/types/ui/RegistryElementBrowser/RegistryElementBrowserCategories.d.ts +14 -0
- package/dist/types/ui/RegistryElementBrowser/RegistryElementBrowserFooter.d.ts +9 -0
- package/dist/types/ui/RegistryElementBrowser/RegistryElementBrowserSearchResults.d.ts +31 -0
- package/dist/types/ui/RegistryElementBrowser/index.d.ts +9 -0
- package/dist/types/ui/RegistryElementBrowser/model.d.ts +31 -0
- package/package.json +13 -8
- package/registry-element-browser/package.json +10 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.getRegistryElementBrowserItems = exports.getInitialRegistryElementBrowserSection = exports.createRegistryElementBrowserModel = void 0;
|
|
8
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
+
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
10
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
11
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
12
|
+
var compareRanks = function compareRanks(left, right) {
|
|
13
|
+
for (var index = 0; index < Math.max(left.length, right.length); index++) {
|
|
14
|
+
var _left$index, _right$index;
|
|
15
|
+
var difference = ((_left$index = left[index]) !== null && _left$index !== void 0 ? _left$index : 0) - ((_right$index = right[index]) !== null && _right$index !== void 0 ? _right$index : 0);
|
|
16
|
+
if (difference !== 0) {
|
|
17
|
+
return difference;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return 0;
|
|
21
|
+
};
|
|
22
|
+
var isVisible = function isVisible(registration) {
|
|
23
|
+
var _registration$isHidde;
|
|
24
|
+
return ((_registration$isHidde = registration.isHidden) === null || _registration$isHidde === void 0 ? void 0 : _registration$isHidde.call(registration)) !== true;
|
|
25
|
+
};
|
|
26
|
+
var getChildren = function getChildren(components, parentKey) {
|
|
27
|
+
return components.filter(function (component) {
|
|
28
|
+
var _component$parents;
|
|
29
|
+
return isVisible(component) && ((_component$parents = component.parents) === null || _component$parents === void 0 ? void 0 : _component$parents.some(function (parent) {
|
|
30
|
+
return parent.key === parentKey;
|
|
31
|
+
}));
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
var getParentRank = function getParentRank(registration, parentKey) {
|
|
35
|
+
var _registration$parents, _registration$parents2;
|
|
36
|
+
return (_registration$parents = (_registration$parents2 = registration.parents) === null || _registration$parents2 === void 0 || (_registration$parents2 = _registration$parents2.find(function (parent) {
|
|
37
|
+
return parent.key === parentKey;
|
|
38
|
+
})) === null || _registration$parents2 === void 0 ? void 0 : _registration$parents2.rank) !== null && _registration$parents !== void 0 ? _registration$parents : Number.MAX_SAFE_INTEGER;
|
|
39
|
+
};
|
|
40
|
+
var _getDescendantItems = function getDescendantItems(components, parentKey, ancestorRanks, sectionKey) {
|
|
41
|
+
return getChildren(components, parentKey).flatMap(function (component) {
|
|
42
|
+
var ranks = [].concat((0, _toConsumableArray2.default)(ancestorRanks), [getParentRank(component, parentKey)]);
|
|
43
|
+
if (component.type === 'menu-item') {
|
|
44
|
+
return component.component ? [{
|
|
45
|
+
ranks: ranks,
|
|
46
|
+
registration: component,
|
|
47
|
+
sectionKeys: [sectionKey]
|
|
48
|
+
}] : [];
|
|
49
|
+
}
|
|
50
|
+
return _getDescendantItems(components, component.key, ranks, sectionKey);
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Builds the Element Browser's immutable open-time view of the Quick Insert registry.
|
|
56
|
+
* The caller provides only components rooted at the Quick Insert menu surface.
|
|
57
|
+
*/
|
|
58
|
+
var createRegistryElementBrowserModel = exports.createRegistryElementBrowserModel = function createRegistryElementBrowserModel(components, _ref) {
|
|
59
|
+
var footerKey = _ref.footerKey,
|
|
60
|
+
overflowKeys = _ref.overflowKeys,
|
|
61
|
+
recommendedSectionKey = _ref.recommendedSectionKey,
|
|
62
|
+
rootKey = _ref.rootKey;
|
|
63
|
+
var excludedKeys = new Set([footerKey, recommendedSectionKey].concat((0, _toConsumableArray2.default)(overflowKeys)));
|
|
64
|
+
var visibleComponents = components.filter(function (component) {
|
|
65
|
+
return isVisible(component) && !excludedKeys.has(component.key);
|
|
66
|
+
});
|
|
67
|
+
var sections = getChildren(visibleComponents, rootKey).filter(function (component) {
|
|
68
|
+
return component.type === 'menu-section';
|
|
69
|
+
}).map(function (section) {
|
|
70
|
+
return {
|
|
71
|
+
key: section.key,
|
|
72
|
+
rank: getParentRank(section, rootKey)
|
|
73
|
+
};
|
|
74
|
+
}).sort(function (left, right) {
|
|
75
|
+
return left.rank - right.rank;
|
|
76
|
+
});
|
|
77
|
+
var itemByKey = new Map();
|
|
78
|
+
sections.forEach(function (section) {
|
|
79
|
+
_getDescendantItems(visibleComponents, section.key, [section.rank], section.key).forEach(function (item) {
|
|
80
|
+
var existing = itemByKey.get(item.registration.key);
|
|
81
|
+
if (!existing) {
|
|
82
|
+
itemByKey.set(item.registration.key, item);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
var sectionKeys = (0, _toConsumableArray2.default)(new Set([].concat((0, _toConsumableArray2.default)(existing.sectionKeys), (0, _toConsumableArray2.default)(item.sectionKeys))));
|
|
86
|
+
itemByKey.set(item.registration.key, compareRanks(item.ranks, existing.ranks) < 0 ? _objectSpread(_objectSpread({}, item), {}, {
|
|
87
|
+
sectionKeys: sectionKeys
|
|
88
|
+
}) : _objectSpread(_objectSpread({}, existing), {}, {
|
|
89
|
+
sectionKeys: sectionKeys
|
|
90
|
+
}));
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
return {
|
|
94
|
+
items: (0, _toConsumableArray2.default)(itemByKey.values()).sort(function (left, right) {
|
|
95
|
+
return compareRanks(left.ranks, right.ranks);
|
|
96
|
+
}),
|
|
97
|
+
sections: sections
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
var getInitialRegistryElementBrowserSection = exports.getInitialRegistryElementBrowserSection = function getInitialRegistryElementBrowserSection(model, requestedSection) {
|
|
101
|
+
return model.sections.some(function (section) {
|
|
102
|
+
return section.key === requestedSection;
|
|
103
|
+
}) ? requestedSection : undefined;
|
|
104
|
+
};
|
|
105
|
+
var getRegistryElementBrowserItems = exports.getRegistryElementBrowserItems = function getRegistryElementBrowserItems(_ref2) {
|
|
106
|
+
var formatMessage = _ref2.formatMessage,
|
|
107
|
+
model = _ref2.model,
|
|
108
|
+
query = _ref2.query,
|
|
109
|
+
section = _ref2.section;
|
|
110
|
+
var normalizedQuery = query.trim();
|
|
111
|
+
var items = section ? model.items.filter(function (item) {
|
|
112
|
+
return item.sectionKeys.includes(section);
|
|
113
|
+
}) : model.items;
|
|
114
|
+
if (!normalizedQuery) {
|
|
115
|
+
return items;
|
|
116
|
+
}
|
|
117
|
+
return items.flatMap(function (item) {
|
|
118
|
+
var _item$registration$ma, _item$registration;
|
|
119
|
+
var result = (_item$registration$ma = (_item$registration = item.registration).match) === null || _item$registration$ma === void 0 ? void 0 : _item$registration$ma.call(_item$registration, {
|
|
120
|
+
formatMessage: formatMessage,
|
|
121
|
+
query: normalizedQuery
|
|
122
|
+
});
|
|
123
|
+
return result ? [_objectSpread(_objectSpread({}, item), {}, {
|
|
124
|
+
score: Math.min(1, Math.max(0, result.score))
|
|
125
|
+
})] : [];
|
|
126
|
+
}).sort(function (left, right) {
|
|
127
|
+
return left.score - right.score || compareRanks(left.ranks, right.ranks) || left.registration.key.localeCompare(right.registration.key);
|
|
128
|
+
});
|
|
129
|
+
};
|
|
@@ -16,6 +16,7 @@ import { pluginKey } from './pm-plugins/plugin-key';
|
|
|
16
16
|
import { getLegacyCompatibleComponents } from './ui/getLegacyCompatibleComponents';
|
|
17
17
|
import { getQuickInsertComponents } from './ui/getQuickInsertComponents';
|
|
18
18
|
import ModalElementBrowser from './ui/ModalElementBrowser';
|
|
19
|
+
import { RegistryElementBrowserContainer } from './ui/RegistryElementBrowser';
|
|
19
20
|
import { getQuickInsertSuggestions, withLayoutQuickInsertPrioritySorting } from './ui/search';
|
|
20
21
|
export const quickInsertPlugin = ({
|
|
21
22
|
config: options,
|
|
@@ -79,7 +80,7 @@ export const quickInsertPlugin = ({
|
|
|
79
80
|
return {
|
|
80
81
|
title: formatMessage(messages.viewMore),
|
|
81
82
|
ariaLabel: formatMessage(messages.viewMoreAriaLabel),
|
|
82
|
-
onClick:
|
|
83
|
+
onClick: props => openElementBrowser()(props),
|
|
83
84
|
iconBefore: /*#__PURE__*/React.createElement(ShowMoreHorizontalIcon, {
|
|
84
85
|
label: ""
|
|
85
86
|
})
|
|
@@ -106,7 +107,7 @@ export const quickInsertPlugin = ({
|
|
|
106
107
|
tr
|
|
107
108
|
});
|
|
108
109
|
}
|
|
109
|
-
return openElementBrowserCommand(options)({
|
|
110
|
+
return (isExperimentEnabled('platform_editor_slash_command') ? openElementBrowserCommand(options) : openElementBrowserModal)({
|
|
110
111
|
tr
|
|
111
112
|
});
|
|
112
113
|
};
|
|
@@ -148,7 +149,11 @@ export const quickInsertPlugin = ({
|
|
|
148
149
|
return null;
|
|
149
150
|
}
|
|
150
151
|
if (options !== null && options !== void 0 && options.enableElementBrowser) {
|
|
151
|
-
return /*#__PURE__*/React.createElement(
|
|
152
|
+
return isExperimentEnabled('platform_editor_slash_command') && api !== null && api !== void 0 && api.uiControlRegistry ? /*#__PURE__*/React.createElement(RegistryElementBrowserContainer, {
|
|
153
|
+
editorView: editorView,
|
|
154
|
+
helpUrl: options === null || options === void 0 ? void 0 : options.elementBrowserHelpUrl,
|
|
155
|
+
pluginInjectionAPI: api
|
|
156
|
+
}) : /*#__PURE__*/React.createElement(ModalElementBrowser, {
|
|
152
157
|
editorView: editorView,
|
|
153
158
|
helpUrl: options === null || options === void 0 ? void 0 : options.elementBrowserHelpUrl,
|
|
154
159
|
pluginInjectionAPI: api
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/* RegistryElementBrowser.tsx generated by @compiled/babel-plugin v2.0.0 */
|
|
2
|
+
import "./RegistryElementBrowser.compiled.css";
|
|
3
|
+
import { ax, ix } from "@compiled/react/runtime";
|
|
4
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
5
|
+
import { useIntl } from 'react-intl';
|
|
6
|
+
import Heading from '@atlaskit/heading/heading';
|
|
7
|
+
import { MENU, RECOMMENDED_SECTION } from '@atlaskit/editor-common/quick-insert/keys';
|
|
8
|
+
import { getMenuFooterSectionKey } from '@atlaskit/editor-common/type-ahead-get-menu-footer-section-key';
|
|
9
|
+
import { getSectionOverflowItemKey } from '@atlaskit/editor-common/type-ahead-get-section-overflow-item-key';
|
|
10
|
+
import Modal, { ModalBody, ModalFooter, ModalHeader, ModalTransition } from '@atlaskit/modal-dialog';
|
|
11
|
+
import { createRegistryElementBrowserModel, getInitialRegistryElementBrowserSection, getRegistryElementBrowserItems } from './model';
|
|
12
|
+
import { RegistryElementBrowserCategories } from './RegistryElementBrowserCategories';
|
|
13
|
+
import { RegistryElementBrowserFooter } from './RegistryElementBrowserFooter';
|
|
14
|
+
import { RegistryElementBrowserSearchResults } from './RegistryElementBrowserSearchResults';
|
|
15
|
+
const browserLayoutStyles = null;
|
|
16
|
+
const resultsId = 'registry-element-browser-results';
|
|
17
|
+
export const RegistryElementBrowser = ({
|
|
18
|
+
components,
|
|
19
|
+
emptyStateHandler,
|
|
20
|
+
defaultSection,
|
|
21
|
+
helpUrl,
|
|
22
|
+
editorView,
|
|
23
|
+
isOffline,
|
|
24
|
+
isOpen,
|
|
25
|
+
onClearSelection,
|
|
26
|
+
onClose,
|
|
27
|
+
onCloseComplete,
|
|
28
|
+
onConfirmInsert,
|
|
29
|
+
onSelect
|
|
30
|
+
}) => {
|
|
31
|
+
const {
|
|
32
|
+
formatMessage
|
|
33
|
+
} = useIntl();
|
|
34
|
+
const model = useMemo(() => createRegistryElementBrowserModel(components, {
|
|
35
|
+
footerKey: getMenuFooterSectionKey(MENU.key),
|
|
36
|
+
overflowKeys: new Set(components.filter(component => component.type === 'menu-section').map(component => getSectionOverflowItemKey(component.key))),
|
|
37
|
+
recommendedSectionKey: RECOMMENDED_SECTION.key,
|
|
38
|
+
rootKey: MENU.key
|
|
39
|
+
}), [components]);
|
|
40
|
+
const [query, setQuery] = useState('');
|
|
41
|
+
const [section, setSection] = useState(() => getInitialRegistryElementBrowserSection(model, defaultSection));
|
|
42
|
+
const [selectedKey, setSelectedKey] = useState();
|
|
43
|
+
const wasOpen = useRef(false);
|
|
44
|
+
const previousIsOffline = useRef(isOffline);
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (isOpen && !wasOpen.current) {
|
|
47
|
+
setQuery('');
|
|
48
|
+
setSection(getInitialRegistryElementBrowserSection(model, defaultSection));
|
|
49
|
+
// This clears local visual state and the parent-held insertion handler together.
|
|
50
|
+
// eslint-disable-next-line @atlassian/perf-linting/no-chain-state-updates
|
|
51
|
+
setSelectedKey(undefined);
|
|
52
|
+
onClearSelection();
|
|
53
|
+
}
|
|
54
|
+
wasOpen.current = isOpen;
|
|
55
|
+
}, [defaultSection, isOpen, model, onClearSelection]);
|
|
56
|
+
const items = useMemo(() => getRegistryElementBrowserItems({
|
|
57
|
+
formatMessage,
|
|
58
|
+
model,
|
|
59
|
+
query,
|
|
60
|
+
section: query ? undefined : section
|
|
61
|
+
}), [formatMessage, model, query, section]);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (previousIsOffline.current !== isOffline) {
|
|
64
|
+
setSelectedKey(undefined);
|
|
65
|
+
onClearSelection();
|
|
66
|
+
}
|
|
67
|
+
previousIsOffline.current = isOffline;
|
|
68
|
+
}, [isOffline, onClearSelection]);
|
|
69
|
+
const onSearchChange = useCallback(event => {
|
|
70
|
+
setQuery(event.currentTarget.value);
|
|
71
|
+
setSection(undefined);
|
|
72
|
+
setSelectedKey(undefined);
|
|
73
|
+
onClearSelection();
|
|
74
|
+
}, [onClearSelection]);
|
|
75
|
+
const onSectionClick = useCallback(event => {
|
|
76
|
+
setQuery('');
|
|
77
|
+
setSection(event.currentTarget.dataset.section || undefined);
|
|
78
|
+
setSelectedKey(undefined);
|
|
79
|
+
onClearSelection();
|
|
80
|
+
}, [onClearSelection]);
|
|
81
|
+
const onRegistrySelect = useCallback((key, handler) => {
|
|
82
|
+
setSelectedKey(key);
|
|
83
|
+
onSelect(handler);
|
|
84
|
+
}, [onSelect]);
|
|
85
|
+
const onKeyDown = useCallback(event => {
|
|
86
|
+
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
87
|
+
var _resultCollection$que;
|
|
88
|
+
const resultCollection = event.currentTarget.ownerDocument.getElementById(resultsId);
|
|
89
|
+
const options = Array.from((_resultCollection$que = resultCollection === null || resultCollection === void 0 ? void 0 : resultCollection.querySelectorAll('[role="option"]:not([aria-disabled="true"]):not([disabled])')) !== null && _resultCollection$que !== void 0 ? _resultCollection$que : []);
|
|
90
|
+
const currentIndex = options.findIndex(option => option === event.currentTarget.ownerDocument.activeElement);
|
|
91
|
+
const nextIndex = event.key === 'ArrowDown' ? currentIndex + 1 : currentIndex - 1;
|
|
92
|
+
const nextOption = options.at(nextIndex);
|
|
93
|
+
if (nextOption) {
|
|
94
|
+
event.preventDefault();
|
|
95
|
+
nextOption.focus();
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (event.key === 'Escape') {
|
|
100
|
+
event.preventDefault();
|
|
101
|
+
onClose();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (event.key === 'Enter' && selectedKey) {
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
onConfirmInsert();
|
|
107
|
+
}
|
|
108
|
+
}, [onClose, onConfirmInsert, selectedKey]);
|
|
109
|
+
return /*#__PURE__*/React.createElement(ModalTransition, null, isOpen && /*#__PURE__*/React.createElement(Modal, {
|
|
110
|
+
label: formatMessage({
|
|
111
|
+
defaultMessage: 'Insert elements',
|
|
112
|
+
id: 'editor.quick-insert.title'
|
|
113
|
+
}),
|
|
114
|
+
onClose: onClose,
|
|
115
|
+
onCloseComplete: onCloseComplete,
|
|
116
|
+
width: "x-large"
|
|
117
|
+
}, /*#__PURE__*/React.createElement(ModalHeader, null, /*#__PURE__*/React.createElement(Heading, {
|
|
118
|
+
size: "medium"
|
|
119
|
+
}, formatMessage({
|
|
120
|
+
defaultMessage: 'Insert elements',
|
|
121
|
+
id: 'editor.quick-insert.title'
|
|
122
|
+
}))), /*#__PURE__*/React.createElement(ModalBody, null, /*#__PURE__*/React.createElement("div", {
|
|
123
|
+
className: ax(["_zulp1ejb _1e0c11p5 _yv0eo9bh"])
|
|
124
|
+
}, /*#__PURE__*/React.createElement(RegistryElementBrowserCategories, {
|
|
125
|
+
query: query,
|
|
126
|
+
section: section,
|
|
127
|
+
sections: model.sections,
|
|
128
|
+
onSectionClick: onSectionClick
|
|
129
|
+
}), /*#__PURE__*/React.createElement(RegistryElementBrowserSearchResults, {
|
|
130
|
+
editorView: editorView,
|
|
131
|
+
emptyStateHandler: emptyStateHandler,
|
|
132
|
+
isOffline: isOffline,
|
|
133
|
+
items: items,
|
|
134
|
+
query: query,
|
|
135
|
+
resultsId: resultsId,
|
|
136
|
+
section: section,
|
|
137
|
+
selectedKey: selectedKey,
|
|
138
|
+
onKeyDown: onKeyDown,
|
|
139
|
+
onSearchChange: onSearchChange,
|
|
140
|
+
onSelect: onRegistrySelect
|
|
141
|
+
}))), /*#__PURE__*/React.createElement(ModalFooter, null, /*#__PURE__*/React.createElement(RegistryElementBrowserFooter, {
|
|
142
|
+
helpUrl: helpUrl,
|
|
143
|
+
isInsertEnabled: Boolean(selectedKey),
|
|
144
|
+
onClose: onClose,
|
|
145
|
+
onConfirmInsert: onConfirmInsert
|
|
146
|
+
}))));
|
|
147
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/* RegistryElementBrowserCategories.tsx generated by @compiled/babel-plugin v2.0.0 */
|
|
2
|
+
import "./RegistryElementBrowserCategories.compiled.css";
|
|
3
|
+
import { ax, ix } from "@compiled/react/runtime";
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { useIntl } from 'react-intl';
|
|
6
|
+
import Button from '@atlaskit/button/default/button';
|
|
7
|
+
import { BLOCK_TEMPLATES_SECTION, DATA_AND_CHARTS_SECTION, EMBED_SECTION, MEDIA_SECTION, OTHER_SECTION, ROVO_SECTION, STRUCTURE_SECTION, TEXT_FORMATTING_SECTION } from '@atlaskit/editor-common/quick-insert/keys';
|
|
8
|
+
import { messages } from '@atlaskit/editor-common/quick-insert/messages';
|
|
9
|
+
const sectionMessages = {
|
|
10
|
+
[BLOCK_TEMPLATES_SECTION.key]: messages.categoryBlockTemplates,
|
|
11
|
+
[DATA_AND_CHARTS_SECTION.key]: messages.categoryDataAndCharts,
|
|
12
|
+
[EMBED_SECTION.key]: messages.categoryEmbed,
|
|
13
|
+
[MEDIA_SECTION.key]: messages.categoryMedia,
|
|
14
|
+
[OTHER_SECTION.key]: messages.categoryOther,
|
|
15
|
+
[ROVO_SECTION.key]: messages.categoryRovo,
|
|
16
|
+
[STRUCTURE_SECTION.key]: messages.categoryStructure,
|
|
17
|
+
[TEXT_FORMATTING_SECTION.key]: messages.categoryTextFormatting
|
|
18
|
+
};
|
|
19
|
+
const categoryGridStyles = null;
|
|
20
|
+
export const RegistryElementBrowserCategories = ({
|
|
21
|
+
query,
|
|
22
|
+
section,
|
|
23
|
+
sections,
|
|
24
|
+
onSectionClick
|
|
25
|
+
}) => {
|
|
26
|
+
const {
|
|
27
|
+
formatMessage
|
|
28
|
+
} = useIntl();
|
|
29
|
+
const isAllSelected = section === undefined && !query;
|
|
30
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
31
|
+
"aria-label": formatMessage({
|
|
32
|
+
defaultMessage: 'Element categories',
|
|
33
|
+
id: 'editor.quick-insert.categories-label'
|
|
34
|
+
}),
|
|
35
|
+
"data-testid": "registry-element-browser-categories",
|
|
36
|
+
className: ax(["_zulpu2gc _1e0c11p5 _yv0e9sye"])
|
|
37
|
+
}, /*#__PURE__*/React.createElement(Button, {
|
|
38
|
+
appearance: isAllSelected ? 'primary' : 'subtle',
|
|
39
|
+
"aria-pressed": isAllSelected,
|
|
40
|
+
"data-section": "",
|
|
41
|
+
onClick: onSectionClick
|
|
42
|
+
}, formatMessage({
|
|
43
|
+
defaultMessage: 'All',
|
|
44
|
+
id: 'editor.quick-insert.all'
|
|
45
|
+
})), sections.map(entry => {
|
|
46
|
+
var _sectionMessages;
|
|
47
|
+
return /*#__PURE__*/React.createElement(Button, {
|
|
48
|
+
appearance: section === entry.key && !query ? 'primary' : 'subtle',
|
|
49
|
+
"aria-pressed": section === entry.key && !query,
|
|
50
|
+
"data-section": entry.key,
|
|
51
|
+
key: entry.key,
|
|
52
|
+
onClick: onSectionClick
|
|
53
|
+
}, formatMessage((_sectionMessages = sectionMessages[entry.key]) !== null && _sectionMessages !== void 0 ? _sectionMessages : messages.categoryOther));
|
|
54
|
+
}));
|
|
55
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useIntl } from 'react-intl';
|
|
3
|
+
import Button from '@atlaskit/button/default/button';
|
|
4
|
+
import LinkButton from '@atlaskit/button/link';
|
|
5
|
+
export const RegistryElementBrowserFooter = ({
|
|
6
|
+
helpUrl,
|
|
7
|
+
isInsertEnabled,
|
|
8
|
+
onClose,
|
|
9
|
+
onConfirmInsert
|
|
10
|
+
}) => {
|
|
11
|
+
const {
|
|
12
|
+
formatMessage
|
|
13
|
+
} = useIntl();
|
|
14
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, helpUrl && /*#__PURE__*/React.createElement(LinkButton, {
|
|
15
|
+
appearance: "subtle",
|
|
16
|
+
href: helpUrl,
|
|
17
|
+
target: "_blank"
|
|
18
|
+
}, formatMessage({
|
|
19
|
+
defaultMessage: 'Help',
|
|
20
|
+
id: 'editor.quick-insert.help'
|
|
21
|
+
})), /*#__PURE__*/React.createElement(Button, {
|
|
22
|
+
appearance: "subtle",
|
|
23
|
+
onClick: onClose
|
|
24
|
+
}, formatMessage({
|
|
25
|
+
defaultMessage: 'Close',
|
|
26
|
+
id: 'editor.quick-insert.close'
|
|
27
|
+
})), /*#__PURE__*/React.createElement(Button, {
|
|
28
|
+
appearance: "primary",
|
|
29
|
+
isDisabled: !isInsertEnabled,
|
|
30
|
+
onClick: onConfirmInsert
|
|
31
|
+
}, formatMessage({
|
|
32
|
+
defaultMessage: 'Insert',
|
|
33
|
+
id: 'editor.quick-insert.insert'
|
|
34
|
+
})));
|
|
35
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/* RegistryElementBrowserSearchResults.tsx generated by @compiled/babel-plugin v2.0.0 */
|
|
2
|
+
import "./RegistryElementBrowserSearchResults.compiled.css";
|
|
3
|
+
import { ax, ix } from "@compiled/react/runtime";
|
|
4
|
+
import React, { useMemo } from 'react';
|
|
5
|
+
import { useIntl } from 'react-intl';
|
|
6
|
+
import LinkButton from '@atlaskit/button/link';
|
|
7
|
+
import { QuickInsertProvider } from '@atlaskit/editor-common/quick-insert/provider';
|
|
8
|
+
import { Stack, Text } from '@atlaskit/primitives/compiled';
|
|
9
|
+
import Textfield from '@atlaskit/textfield';
|
|
10
|
+
const resultsGridStyles = null;
|
|
11
|
+
const RegistryElementBrowserItem = ({
|
|
12
|
+
editorView,
|
|
13
|
+
isOffline,
|
|
14
|
+
isSelected,
|
|
15
|
+
item,
|
|
16
|
+
onSelect
|
|
17
|
+
}) => {
|
|
18
|
+
const {
|
|
19
|
+
component: Component,
|
|
20
|
+
key
|
|
21
|
+
} = item.registration;
|
|
22
|
+
const contextValue = useMemo(() => ({
|
|
23
|
+
editorView,
|
|
24
|
+
isOffline,
|
|
25
|
+
item: {
|
|
26
|
+
id: key,
|
|
27
|
+
isSelected
|
|
28
|
+
},
|
|
29
|
+
select: handler => onSelect(key, handler),
|
|
30
|
+
surface: 'element-browser'
|
|
31
|
+
}), [editorView, isOffline, isSelected, key, onSelect]);
|
|
32
|
+
if (!Component) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
return /*#__PURE__*/React.createElement(QuickInsertProvider, {
|
|
36
|
+
value: contextValue
|
|
37
|
+
}, /*#__PURE__*/React.createElement(Component, undefined, null));
|
|
38
|
+
};
|
|
39
|
+
export const RegistryElementBrowserSearchResults = ({
|
|
40
|
+
editorView,
|
|
41
|
+
emptyStateHandler,
|
|
42
|
+
isOffline,
|
|
43
|
+
items,
|
|
44
|
+
query,
|
|
45
|
+
resultsId,
|
|
46
|
+
section,
|
|
47
|
+
selectedKey,
|
|
48
|
+
onKeyDown,
|
|
49
|
+
onSearchChange,
|
|
50
|
+
onSelect
|
|
51
|
+
}) => {
|
|
52
|
+
var _emptyStateHandler;
|
|
53
|
+
const {
|
|
54
|
+
formatMessage
|
|
55
|
+
} = useIntl();
|
|
56
|
+
return /*#__PURE__*/React.createElement(Stack, {
|
|
57
|
+
space: "space.200"
|
|
58
|
+
}, /*#__PURE__*/React.createElement(Textfield, {
|
|
59
|
+
autoFocus: true,
|
|
60
|
+
"aria-controls": resultsId,
|
|
61
|
+
"aria-label": formatMessage({
|
|
62
|
+
defaultMessage: 'Search elements',
|
|
63
|
+
id: 'editor.quick-insert.search'
|
|
64
|
+
}),
|
|
65
|
+
name: "registry-element-browser-search",
|
|
66
|
+
onChange: onSearchChange,
|
|
67
|
+
onKeyDown: onKeyDown,
|
|
68
|
+
value: query
|
|
69
|
+
}), /*#__PURE__*/React.createElement(Text, {
|
|
70
|
+
"aria-live": "polite"
|
|
71
|
+
}, formatMessage({
|
|
72
|
+
defaultMessage: '{count} results',
|
|
73
|
+
id: 'editor.quick-insert.results'
|
|
74
|
+
}, {
|
|
75
|
+
count: items.length
|
|
76
|
+
})), /*#__PURE__*/React.createElement("div", {
|
|
77
|
+
"aria-label": formatMessage({
|
|
78
|
+
defaultMessage: 'Element results',
|
|
79
|
+
id: 'editor.quick-insert.results-label'
|
|
80
|
+
}),
|
|
81
|
+
"data-testid": "registry-element-browser-results",
|
|
82
|
+
id: resultsId,
|
|
83
|
+
onKeyDown: onKeyDown,
|
|
84
|
+
tabIndex: -1,
|
|
85
|
+
role: items.length ? 'listbox' : undefined,
|
|
86
|
+
className: ax(["_zulpu2gc _1e0c11p5 _yv0e13mt"])
|
|
87
|
+
}, items.length ? items.map(item => /*#__PURE__*/React.createElement(RegistryElementBrowserItem, {
|
|
88
|
+
editorView: editorView,
|
|
89
|
+
isOffline: isOffline,
|
|
90
|
+
isSelected: selectedKey === item.registration.key,
|
|
91
|
+
item: item,
|
|
92
|
+
key: item.registration.key,
|
|
93
|
+
onSelect: onSelect
|
|
94
|
+
})) : (_emptyStateHandler = emptyStateHandler === null || emptyStateHandler === void 0 ? void 0 : emptyStateHandler({
|
|
95
|
+
mode: 'full',
|
|
96
|
+
searchTerm: query,
|
|
97
|
+
selectedCategory: section
|
|
98
|
+
})) !== null && _emptyStateHandler !== void 0 ? _emptyStateHandler : /*#__PURE__*/React.createElement(Stack, {
|
|
99
|
+
alignInline: "center",
|
|
100
|
+
space: "space.100"
|
|
101
|
+
}, /*#__PURE__*/React.createElement(Text, null, formatMessage({
|
|
102
|
+
defaultMessage: 'Nothing matches your search',
|
|
103
|
+
id: 'editor.quick-insert.empty'
|
|
104
|
+
})), /*#__PURE__*/React.createElement(LinkButton, {
|
|
105
|
+
appearance: "primary",
|
|
106
|
+
href: "https://marketplace.atlassian.com/search?category=Macros&hosting=cloud&product=confluence",
|
|
107
|
+
target: "_blank"
|
|
108
|
+
}, formatMessage({
|
|
109
|
+
defaultMessage: 'Explore Atlassian Marketplace',
|
|
110
|
+
id: 'editor.quick-insert.explore-marketplace'
|
|
111
|
+
})))));
|
|
112
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { ACTION, ACTION_SUBJECT, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
|
|
3
|
+
import { isOfflineMode } from '@atlaskit/editor-common/connectivity/isOfflineMode';
|
|
4
|
+
import { insertSelectedItem } from '@atlaskit/editor-common/insert';
|
|
5
|
+
import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
|
|
6
|
+
import { MENU } from '@atlaskit/editor-common/quick-insert/keys';
|
|
7
|
+
import { closeElementBrowser } from '../../pm-plugins/commands';
|
|
8
|
+
import { pluginKey } from '../../pm-plugins/plugin-key';
|
|
9
|
+
import { RegistryElementBrowser } from './RegistryElementBrowser';
|
|
10
|
+
export const RegistryElementBrowserContainer = ({
|
|
11
|
+
editorView,
|
|
12
|
+
pluginInjectionAPI,
|
|
13
|
+
helpUrl
|
|
14
|
+
}) => {
|
|
15
|
+
var _pluginKey$getState;
|
|
16
|
+
const {
|
|
17
|
+
emptyStateHandler: currentEmptyStateHandler,
|
|
18
|
+
isElementBrowserOpen,
|
|
19
|
+
mode
|
|
20
|
+
} = useSharedPluginStateWithSelector(pluginInjectionAPI, ['quickInsert', 'connectivity'], state => {
|
|
21
|
+
var _state$quickInsertSta, _state$quickInsertSta2, _state$quickInsertSta3, _state$connectivitySt;
|
|
22
|
+
return {
|
|
23
|
+
isElementBrowserOpen: (_state$quickInsertSta = (_state$quickInsertSta2 = state.quickInsertState) === null || _state$quickInsertSta2 === void 0 ? void 0 : _state$quickInsertSta2.isElementBrowserOpen) !== null && _state$quickInsertSta !== void 0 ? _state$quickInsertSta : false,
|
|
24
|
+
emptyStateHandler: (_state$quickInsertSta3 = state.quickInsertState) === null || _state$quickInsertSta3 === void 0 ? void 0 : _state$quickInsertSta3.emptyStateHandler,
|
|
25
|
+
mode: (_state$connectivitySt = state.connectivityState) === null || _state$connectivitySt === void 0 ? void 0 : _state$connectivitySt.mode
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
const [snapshot, setSnapshot] = useState([]);
|
|
29
|
+
const [emptyStateHandler, setEmptyStateHandler] = useState();
|
|
30
|
+
const wasOpen = useRef(false);
|
|
31
|
+
const selectionHandler = useRef(undefined);
|
|
32
|
+
const isInsertConfirmed = useRef(false);
|
|
33
|
+
const lifecycleOpen = useRef(false);
|
|
34
|
+
const fireLifecycleEvent = useCallback(action => {
|
|
35
|
+
var _pluginInjectionAPI$a;
|
|
36
|
+
pluginInjectionAPI === null || pluginInjectionAPI === void 0 ? void 0 : (_pluginInjectionAPI$a = pluginInjectionAPI.analytics) === null || _pluginInjectionAPI$a === void 0 ? void 0 : _pluginInjectionAPI$a.actions.fireAnalyticsEvent({
|
|
37
|
+
action,
|
|
38
|
+
actionSubject: ACTION_SUBJECT.ELEMENT_BROWSER,
|
|
39
|
+
attributes: {
|
|
40
|
+
mode: 'full'
|
|
41
|
+
},
|
|
42
|
+
eventType: EVENT_TYPE.UI
|
|
43
|
+
});
|
|
44
|
+
}, [pluginInjectionAPI]);
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (isElementBrowserOpen && !wasOpen.current) {
|
|
47
|
+
var _pluginInjectionAPI$u, _pluginInjectionAPI$u2;
|
|
48
|
+
setSnapshot((_pluginInjectionAPI$u = pluginInjectionAPI === null || pluginInjectionAPI === void 0 ? void 0 : (_pluginInjectionAPI$u2 = pluginInjectionAPI.uiControlRegistry) === null || _pluginInjectionAPI$u2 === void 0 ? void 0 : _pluginInjectionAPI$u2.actions.getComponents(MENU.key)) !== null && _pluginInjectionAPI$u !== void 0 ? _pluginInjectionAPI$u : []);
|
|
49
|
+
selectionHandler.current = undefined;
|
|
50
|
+
setEmptyStateHandler(() => currentEmptyStateHandler);
|
|
51
|
+
isInsertConfirmed.current = false;
|
|
52
|
+
lifecycleOpen.current = true;
|
|
53
|
+
fireLifecycleEvent(ACTION.OPENED);
|
|
54
|
+
} else if (!isElementBrowserOpen && wasOpen.current && lifecycleOpen.current) {
|
|
55
|
+
lifecycleOpen.current = false;
|
|
56
|
+
fireLifecycleEvent(ACTION.CLOSED);
|
|
57
|
+
}
|
|
58
|
+
wasOpen.current = isElementBrowserOpen;
|
|
59
|
+
}, [currentEmptyStateHandler, fireLifecycleEvent, isElementBrowserOpen, pluginInjectionAPI]);
|
|
60
|
+
useEffect(() => () => {
|
|
61
|
+
if (lifecycleOpen.current) {
|
|
62
|
+
lifecycleOpen.current = false;
|
|
63
|
+
fireLifecycleEvent(ACTION.CLOSED);
|
|
64
|
+
}
|
|
65
|
+
}, [fireLifecycleEvent]);
|
|
66
|
+
const focusInEditor = useCallback(() => {
|
|
67
|
+
if (!editorView.hasFocus()) {
|
|
68
|
+
editorView.focus();
|
|
69
|
+
}
|
|
70
|
+
}, [editorView]);
|
|
71
|
+
const onClose = useCallback(() => {
|
|
72
|
+
if (!isInsertConfirmed.current) {
|
|
73
|
+
selectionHandler.current = undefined;
|
|
74
|
+
}
|
|
75
|
+
closeElementBrowser()(editorView.state, editorView.dispatch);
|
|
76
|
+
focusInEditor();
|
|
77
|
+
}, [editorView, focusInEditor]);
|
|
78
|
+
const onCloseComplete = useCallback(() => {
|
|
79
|
+
const handler = isInsertConfirmed.current ? selectionHandler.current : undefined;
|
|
80
|
+
selectionHandler.current = undefined;
|
|
81
|
+
isInsertConfirmed.current = false;
|
|
82
|
+
focusInEditor();
|
|
83
|
+
if (!handler) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const insert = (maybeNode, options = {}) => insertSelectedItem(maybeNode, options)(editorView.state, editorView.state.tr, editorView.state.selection.head);
|
|
87
|
+
const transaction = handler({
|
|
88
|
+
insert,
|
|
89
|
+
source: INPUT_METHOD.ELEMENT_BROWSER
|
|
90
|
+
});
|
|
91
|
+
if (transaction) {
|
|
92
|
+
editorView.dispatch(transaction);
|
|
93
|
+
}
|
|
94
|
+
}, [editorView, focusInEditor]);
|
|
95
|
+
const onSelect = useCallback(handler => {
|
|
96
|
+
selectionHandler.current = handler;
|
|
97
|
+
}, []);
|
|
98
|
+
const onClearSelection = useCallback(() => {
|
|
99
|
+
selectionHandler.current = undefined;
|
|
100
|
+
isInsertConfirmed.current = false;
|
|
101
|
+
}, []);
|
|
102
|
+
const onConfirmInsert = useCallback(() => {
|
|
103
|
+
if (!selectionHandler.current) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
isInsertConfirmed.current = true;
|
|
107
|
+
onClose();
|
|
108
|
+
}, [onClose]);
|
|
109
|
+
return /*#__PURE__*/React.createElement(RegistryElementBrowser, {
|
|
110
|
+
components: snapshot,
|
|
111
|
+
emptyStateHandler: emptyStateHandler,
|
|
112
|
+
helpUrl: helpUrl,
|
|
113
|
+
defaultSection: (_pluginKey$getState = pluginKey.getState(editorView.state)) === null || _pluginKey$getState === void 0 ? void 0 : _pluginKey$getState.elementBrowserInitialCategory,
|
|
114
|
+
editorView: editorView,
|
|
115
|
+
isOffline: isOfflineMode(mode),
|
|
116
|
+
isOpen: isElementBrowserOpen,
|
|
117
|
+
onClearSelection: onClearSelection,
|
|
118
|
+
onClose: onClose,
|
|
119
|
+
onCloseComplete: onCloseComplete,
|
|
120
|
+
onConfirmInsert: onConfirmInsert,
|
|
121
|
+
onSelect: onSelect
|
|
122
|
+
});
|
|
123
|
+
};
|