@atlaskit/editor-common
Version:
A package that contains common classes and components for editor and renderer
936 lines (934 loc) • 44.8 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.visuallyHiddenStyles = exports.messages = exports.default = exports.RECENT_SEARCH_LIST_SIZE = exports.HyperlinkLinkAddToolbarWithIntl = exports.HyperlinkLinkAddToolbar = void 0;
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _taggedTemplateLiteral2 = _interopRequireDefault(require("@babel/runtime/helpers/taggedTemplateLiteral"));
var _react = _interopRequireWildcard(require("react"));
var _react2 = require("@emotion/react");
var _debounce = _interopRequireDefault(require("lodash/debounce"));
var _reactDom = require("react-dom");
var _reactIntlNext = require("react-intl-next");
var _adfSchema = require("@atlaskit/adf-schema");
var _analyticsNext = require("@atlaskit/analytics-next");
var _ = _interopRequireDefault(require("@atlaskit/icon-object/glyph/page/16"));
var _crossCircle = _interopRequireDefault(require("@atlaskit/icon/glyph/cross-circle"));
var _colors = require("@atlaskit/theme/colors");
var _constants = require("@atlaskit/theme/constants");
var _tooltip = _interopRequireDefault(require("@atlaskit/tooltip"));
var _analytics = require("../../../analytics");
var _ui = require("../../../ui");
var _utils = require("../../../utils");
var _LinkSearchList = _interopRequireDefault(require("../../LinkSearch/LinkSearchList"));
var _ToolbarComponents = require("../../LinkSearch/ToolbarComponents");
var _transformTimeStamp = require("../../LinkSearch/transformTimeStamp");
var _utils2 = require("./utils");
var _templateObject, _templateObject2, _templateObject3, _templateObject4, _templateObject5, _templateObject6, _templateObject7;
/** @jsx jsx */
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); }
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 && Object.prototype.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; }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
/**
* Visible only to screenreaders. Use when there is a need
* to provide more context to a non-sighted user.
*/
var visuallyHiddenStyles = exports.visuallyHiddenStyles = (0, _react2.css)(_templateObject || (_templateObject = (0, _taggedTemplateLiteral2.default)(["\n clip: rect(1px, 1px, 1px, 1px);\n clip-path: inset(50%);\n height: 1px;\n width: 1px;\n margin: ", ";\n overflow: hidden;\n padding: 0;\n position: absolute;\n"])), "var(--ds-space-negative-025, -2px)");
var RECENT_SEARCH_LIST_SIZE = exports.RECENT_SEARCH_LIST_SIZE = 5;
var clearText = (0, _react2.css)(_templateObject2 || (_templateObject2 = (0, _taggedTemplateLiteral2.default)(["\n cursor: pointer;\n padding: 0;\n margin-right: ", ";\n color: ", ";\n background: transparent;\n border: none;\n"])), "var(--ds-space-100, 8px)", "var(--ds-icon-subtle, ".concat(_colors.N90, ")"));
var clearTextWrapper = (0, _react2.css)(_templateObject3 || (_templateObject3 = (0, _taggedTemplateLiteral2.default)(["\n position: absolute;\n right: 0;\n"])));
var containerPadding = (0, _react2.css)(_templateObject4 || (_templateObject4 = (0, _taggedTemplateLiteral2.default)(["\n padding: ", " ", ";\n"])), "var(--ds-space-150, 12px)", "var(--ds-space-100, 8px)");
var textLabelMargin = (0, _react2.css)(_templateObject5 || (_templateObject5 = (0, _taggedTemplateLiteral2.default)(["\n margin-top: ", ";\n"])), "var(--ds-space-150, 12px)");
var inputLabel = (0, _react2.css)(_templateObject6 || (_templateObject6 = (0, _taggedTemplateLiteral2.default)(["\n font-size: ", "px;\n color: ", ";\n font-weight: 500;\n padding-bottom: ", ";\n"])), (0, _constants.fontSizeSmall)(), "var(--ds-text-subtlest, ".concat(_colors.N200, ")"), "var(--ds-space-050, 4px)");
var inputWrapperPosition = (0, _react2.css)(_templateObject7 || (_templateObject7 = (0, _taggedTemplateLiteral2.default)(["\n position: relative;\n"])));
var messages = exports.messages = (0, _reactIntlNext.defineMessages)({
displayText: {
id: 'fabric.editor.displayText',
defaultMessage: 'Text to display',
description: 'Text to display'
},
clearText: {
id: 'fabric.editor.clearLinkText',
defaultMessage: 'Clear text',
description: 'Clears text on the link toolbar'
},
clearLink: {
id: 'fabric.editor.clearLink',
defaultMessage: 'Clear link',
description: 'Clears link in the link toolbar'
},
searchLinkAriaDescription: {
id: 'fabric.editor.hyperlink.searchLinkAriaDescription',
defaultMessage: 'Suggestions will appear below as you type into the field',
description: 'Describes what the search field does for screen reader users.'
},
searchLinkResults: {
id: 'fabric.editor.hyperlink.searchLinkResults',
defaultMessage: '{count, plural, =0 {no results} one {# result} other {# results}} found',
description: 'Announce search results for screen-reader users.'
},
linkVisibleLabel: {
id: 'fabric.editor.hyperlink.linkVisibleLabel',
defaultMessage: 'Paste or search for link',
description: 'Visible label for link input in hyperlink floating control'
},
textVisibleLabel: {
id: 'fabric.editor.hyperlink.textVisibleLabel',
defaultMessage: 'Display text (optional)',
description: 'Visible label for text input in hyperlink floating control'
}
});
var defaultIcon = (0, _react2.jsx)(_.default, {
label: 'page'
});
var mapActivityProviderResultToLinkSearchItemData = function mapActivityProviderResultToLinkSearchItemData(_ref) {
var name = _ref.name,
container = _ref.container,
iconUrl = _ref.iconUrl,
objectId = _ref.objectId,
url = _ref.url,
viewedTimestamp = _ref.viewedTimestamp;
return {
objectId: objectId,
name: name,
container: container,
iconUrl: iconUrl,
url: url,
lastViewedDate: viewedTimestamp ? new Date(viewedTimestamp) : undefined,
prefetch: true
};
};
var mapSearchProviderResultToLinkSearchItemData = function mapSearchProviderResultToLinkSearchItemData(_ref2) {
var objectId = _ref2.objectId,
container = _ref2.container,
title = _ref2.title,
contentType = _ref2.contentType,
url = _ref2.url,
updatedTimestamp = _ref2.updatedTimestamp;
return {
objectId: objectId,
container: container,
name: title,
url: url,
lastUpdatedDate: updatedTimestamp ? new Date(updatedTimestamp) : undefined,
icon: contentType && _utils2.mapContentTypeToIcon[contentType] || defaultIcon,
prefetch: false
};
};
var HyperlinkLinkAddToolbar = exports.HyperlinkLinkAddToolbar = /*#__PURE__*/function (_PureComponent) {
(0, _inherits2.default)(HyperlinkLinkAddToolbar, _PureComponent);
var _super = _createSuper(HyperlinkLinkAddToolbar);
function HyperlinkLinkAddToolbar(props) {
var _this;
(0, _classCallCheck2.default)(this, HyperlinkLinkAddToolbar);
_this = _super.call(this, props);
/* To prevent double submit */
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "submitted", false);
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "urlInputContainer", null);
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "displayTextInputContainer", null);
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "wrapperRef", /*#__PURE__*/_react.default.createRef());
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "quickSearchQueryVersion", 0);
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "analyticSource", 'createLinkInlineDialog');
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "quickSearch", /*#__PURE__*/function () {
var _ref3 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(input, items, quickSearchLimit) {
var _pluginState$searchSe;
var _this$state, searchProvider, displayUrl, pluginState, queryVersion, perfStart, _pluginState$searchSe2, searchProviderResultItems, searchItems, perfStop, duration, _perfStop, _duration;
return _regenerator.default.wrap(function _callee$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
_this$state = _this.state, searchProvider = _this$state.searchProvider, displayUrl = _this$state.displayUrl;
pluginState = _this.props.pluginState;
if (searchProvider) {
_context.next = 4;
break;
}
return _context.abrupt("return");
case 4:
queryVersion = ++_this.quickSearchQueryVersion;
_this.fireAnalytics({
action: _analytics.ACTION.ENTERED,
actionSubject: _analytics.ACTION_SUBJECT.TEXT,
actionSubjectId: _analytics.ACTION_SUBJECT_ID.LINK_SEARCH_INPUT,
attributes: {
queryLength: input.length,
queryVersion: queryVersion,
queryHash: (0, _utils2.sha1)(input),
searchSessionId: (_pluginState$searchSe = pluginState.searchSessionId) !== null && _pluginState$searchSe !== void 0 ? _pluginState$searchSe : '',
wordCount: (0, _utils2.wordCount)(input),
source: _this.analyticSource
},
nonPrivacySafeAttributes: {
query: input
},
eventType: _analytics.EVENT_TYPE.UI
});
perfStart = performance.now();
_context.prev = 7;
_context.next = 10;
return searchProvider.quickSearch(input, quickSearchLimit);
case 10:
searchProviderResultItems = _context.sent;
searchItems = limit((0, _utils2.filterUniqueItems)([].concat((0, _toConsumableArray2.default)(items), (0, _toConsumableArray2.default)(searchProviderResultItems.map(mapSearchProviderResultToLinkSearchItemData))), function (firstItem, secondItem) {
return firstItem.objectId === secondItem.objectId;
}));
if (displayUrl === input && queryVersion === _this.quickSearchQueryVersion) {
_this.setState({
items: searchItems,
isLoading: false
});
}
perfStop = performance.now();
duration = perfStop - perfStart;
_this.fireAnalytics({
action: _analytics.ACTION.INVOKED,
actionSubject: _analytics.ACTION_SUBJECT.SEARCH_RESULT,
actionSubjectId: _analytics.ACTION_SUBJECT_ID.QUICK_SEARCH,
attributes: {
duration: duration,
count: searchProviderResultItems.length
},
eventType: _analytics.EVENT_TYPE.OPERATIONAL
});
_this.fireAnalytics({
action: _analytics.ACTION.SHOWN,
actionSubject: _analytics.ACTION_SUBJECT.SEARCH_RESULT,
actionSubjectId: _analytics.ACTION_SUBJECT_ID.POST_QUERY_SEARCH_RESULTS,
attributes: {
source: _this.analyticSource,
postQueryRequestDurationMs: duration,
searchSessionId: (_pluginState$searchSe2 = pluginState.searchSessionId) !== null && _pluginState$searchSe2 !== void 0 ? _pluginState$searchSe2 : '',
resultCount: searchProviderResultItems.length,
results: searchProviderResultItems.map(function (item) {
return {
resultContentId: item.objectId,
resultType: item.contentType
};
})
},
eventType: _analytics.EVENT_TYPE.UI
});
_context.next = 24;
break;
case 19:
_context.prev = 19;
_context.t0 = _context["catch"](7);
_perfStop = performance.now();
_duration = _perfStop - perfStart;
_this.fireAnalytics({
action: _analytics.ACTION.INVOKED,
actionSubject: _analytics.ACTION_SUBJECT.SEARCH_RESULT,
actionSubjectId: _analytics.ACTION_SUBJECT_ID.QUICK_SEARCH,
attributes: {
duration: _duration,
count: -1,
errorCode: _context.t0.status
},
eventType: _analytics.EVENT_TYPE.OPERATIONAL
});
case 24:
case "end":
return _context.stop();
}
}, _callee, null, [[7, 19]]);
}));
return function (_x, _x2, _x3) {
return _ref3.apply(this, arguments);
};
}());
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "updateInput", /*#__PURE__*/function () {
var _ref4 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(input) {
var _this$state2, activityProvider, searchProvider, _items, shouldQuerySearchProvider;
return _regenerator.default.wrap(function _callee2$(_context2) {
while (1) switch (_context2.prev = _context2.next) {
case 0:
_this$state2 = _this.state, activityProvider = _this$state2.activityProvider, searchProvider = _this$state2.searchProvider;
_this.setState({
displayUrl: input
});
if (!activityProvider) {
_context2.next = 23;
break;
}
if (!(input.length === 0)) {
_context2.next = 13;
break;
}
_context2.t0 = _this;
_context2.next = 7;
return _this.getRecentItems(activityProvider);
case 7:
_context2.t1 = _context2.sent;
_context2.t2 = -1;
_context2.t3 = {
items: _context2.t1,
selectedIndex: _context2.t2
};
_context2.t0.setState.call(_context2.t0, _context2.t3);
_context2.next = 23;
break;
case 13:
if (!(0, _adfSchema.isSafeUrl)(input)) {
_context2.next = 17;
break;
}
_this.setState({
items: [],
selectedIndex: -1,
isLoading: false
});
_context2.next = 23;
break;
case 17:
_context2.next = 19;
return _this.getRecentItems(activityProvider, input);
case 19:
_items = _context2.sent;
shouldQuerySearchProvider = _items.length < RECENT_SEARCH_LIST_SIZE && !!searchProvider;
_this.setState({
items: _items,
isLoading: shouldQuerySearchProvider
});
if (shouldQuerySearchProvider) {
_this.debouncedQuickSearch(input, _items, RECENT_SEARCH_LIST_SIZE);
}
case 23:
case "end":
return _context2.stop();
}
}, _callee2);
}));
return function (_x4) {
return _ref4.apply(this, arguments);
};
}());
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "createClearHandler", function (field) {
return /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3() {
var activityProvider, _this$setState;
return _regenerator.default.wrap(function _callee3$(_context3) {
while (1) switch (_context3.prev = _context3.next) {
case 0:
activityProvider = _this.state.activityProvider;
_context3.t0 = field;
_context3.next = _context3.t0 === 'displayUrl' ? 4 : _context3.t0 === 'displayText' ? 24 : 26;
break;
case 4:
_context3.t1 = _this;
_this$setState = {};
(0, _defineProperty2.default)(_this$setState, field, '');
_context3.t2 = _defineProperty2.default;
_context3.t3 = _this$setState;
if (!activityProvider) {
_context3.next = 17;
break;
}
_context3.t5 = limit;
_context3.next = 13;
return activityProvider.getRecentItems();
case 13:
_context3.t6 = _context3.sent;
_context3.t4 = (0, _context3.t5)(_context3.t6);
_context3.next = 18;
break;
case 17:
_context3.t4 = [];
case 18:
_context3.t7 = _context3.t4;
(0, _context3.t2)(_context3.t3, "items", _context3.t7);
_context3.t8 = _this$setState;
_context3.t1.setState.call(_context3.t1, _context3.t8);
if (_this.urlInputContainer) {
_this.urlInputContainer.focus();
}
return _context3.abrupt("break", 26);
case 24:
_this.setState((0, _defineProperty2.default)({}, field, ''));
if (_this.displayTextInputContainer) {
_this.displayTextInputContainer.focus();
}
case 26:
case "end":
return _context3.stop();
}
}, _callee3);
}));
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "handleClickOutside", function (event) {
if (event.target instanceof Element && _this.wrapperRef.current && !_this.wrapperRef.current.contains(event.target)) {
var _this$props = _this.props,
_this$props$view = _this$props.view,
state = _this$props$view.state,
dispatch = _this$props$view.dispatch,
onClickAwayCallback = _this$props.onClickAwayCallback;
onClickAwayCallback === null || onClickAwayCallback === void 0 || onClickAwayCallback(state, dispatch);
}
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "getScreenReaderText", function () {
var intl = _this.props.intl;
var _this$state3 = _this.state,
items = _this$state3.items,
selectedIndex = _this$state3.selectedIndex;
if (items.length && selectedIndex > -1) {
var _items$selectedIndex = items[selectedIndex],
name = _items$selectedIndex.name,
_container = _items$selectedIndex.container,
lastUpdatedDate = _items$selectedIndex.lastUpdatedDate,
lastViewedDate = _items$selectedIndex.lastViewedDate;
var date = (0, _transformTimeStamp.transformTimeStamp)(intl, lastViewedDate, lastUpdatedDate);
return "".concat(name, ", ").concat(_container, ", ").concat(date === null || date === void 0 ? void 0 : date.pageAction, " ").concat(date === null || date === void 0 ? void 0 : date.dateString, " ").concat((date === null || date === void 0 ? void 0 : date.timeSince) || '');
}
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "isUrlPopulatedWithSelectedItem", function () {
/**
* When we use ArrowKey to navigate through result items,
* the URL field will be populated with the content of
* selected item.
* This function will check if the URL field is populated
* with selected item.
* It can be useful to detect whether we want to insert a
* smartlink or a hyperlink with customized title
*/
var _this$state4 = _this.state,
items = _this$state4.items,
selectedIndex = _this$state4.selectedIndex,
displayUrl = _this$state4.displayUrl;
var selectedItem = items[selectedIndex];
if (selectedItem && selectedItem.url === displayUrl) {
return true;
}
return false;
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "handleSelected", function (href, text) {
_this.handleInsert(href, text, _analytics.INPUT_METHOD.TYPEAHEAD, 'click');
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "handleInsert", function (href, title, inputType, interaction) {
var _this$props2 = _this.props,
pluginState = _this$props2.pluginState,
onSubmit = _this$props2.onSubmit;
var _this$state5 = _this.state,
items = _this$state5.items,
selectedIndex = _this$state5.selectedIndex,
displayText = _this$state5.displayText;
if (onSubmit) {
_this.submitted = true;
onSubmit(href, title, displayText, inputType);
}
if (interaction === 'click' || _this.isUrlPopulatedWithSelectedItem()) {
var _pluginState$searchSe3, _selectedItem$prefetc;
/**
* When it's a mouse click even or the selectedItem.url matches displayUrl, we think
* it's selected from the result list and fire the
* analytic
*/
var selectedItem = items[selectedIndex];
_this.fireAnalytics({
action: _analytics.ACTION.SELECTED,
actionSubject: _analytics.ACTION_SUBJECT.SEARCH_RESULT,
attributes: {
source: _this.analyticSource,
searchSessionId: (_pluginState$searchSe3 = pluginState.searchSessionId) !== null && _pluginState$searchSe3 !== void 0 ? _pluginState$searchSe3 : '',
trigger: interaction,
resultCount: items.length,
selectedResultId: selectedItem.objectId,
selectedRelativePosition: selectedIndex,
prefetch: (_selectedItem$prefetc = selectedItem.prefetch) !== null && _selectedItem$prefetc !== void 0 ? _selectedItem$prefetc : false
},
eventType: _analytics.EVENT_TYPE.UI
});
}
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "handleMouseEnterResultItem", function (objectId) {
var items = _this.state.items;
var index = findIndex(items, function (item) {
return item.objectId === objectId;
});
_this.setState({
selectedIndex: index
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "handleMouseLeaveResultItem", function (objectId) {
var _this$state6 = _this.state,
items = _this$state6.items,
selectedIndex = _this$state6.selectedIndex;
var index = findIndex(items, function (item) {
return item.objectId === objectId;
});
// This is to avoid updating index that was set by other mouseenter event
if (selectedIndex === index) {
_this.setState({
selectedIndex: -1
});
}
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "handleSubmit", function () {
var _this$state7 = _this.state,
displayUrl = _this$state7.displayUrl,
selectedIndex = _this$state7.selectedIndex,
items = _this$state7.items;
var selectedItem = items[selectedIndex];
if (_this.isUrlPopulatedWithSelectedItem()) {
_this.handleInsert((0, _utils.normalizeUrl)(selectedItem.url), selectedItem.name, _analytics.INPUT_METHOD.TYPEAHEAD, 'keyboard');
} else if (displayUrl && displayUrl.length > 0) {
var url = (0, _utils.normalizeUrl)(displayUrl);
if (url) {
_this.handleInsert(url, displayUrl, _analytics.INPUT_METHOD.MANUAL, 'notselected');
}
}
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "handleClearTextKeyDown", function (event) {
var KEY_CODE_TAB = 9;
var keyCode = event.keyCode;
if (keyCode === KEY_CODE_TAB) {
if (!_this.submitted) {
var _this$state8 = _this.state,
displayUrl = _this$state8.displayUrl,
_displayText = _this$state8.displayText;
var url = (0, _utils.normalizeUrl)(displayUrl);
_this.handleInsert(url, _displayText || displayUrl, _analytics.INPUT_METHOD.MANUAL, 'notselected');
}
event.preventDefault();
return;
}
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "handleKeyDown", function (event) {
var _this$state9 = _this.state,
items = _this$state9.items,
selectedIndex = _this$state9.selectedIndex;
var _this$props3 = _this.props,
pluginState = _this$props3.pluginState,
view = _this$props3.view,
onEscapeCallback = _this$props3.onEscapeCallback;
var keyCode = event.keyCode;
var KEY_CODE_ESCAPE = 27;
var KEY_CODE_ARROW_DOWN = 40;
var KEY_CODE_ARROW_UP = 38;
if (keyCode === KEY_CODE_ESCAPE) {
// escape
event.preventDefault();
var state = view.state,
dispatch = view.dispatch;
onEscapeCallback === null || onEscapeCallback === void 0 || onEscapeCallback(state, dispatch);
return;
}
if (!items || !items.length) {
return;
}
var updatedIndex = selectedIndex;
if (keyCode === KEY_CODE_ARROW_DOWN) {
// down
event.preventDefault();
updatedIndex = (selectedIndex + 1) % items.length;
} else if (keyCode === KEY_CODE_ARROW_UP) {
// up
event.preventDefault();
updatedIndex = selectedIndex > 0 ? selectedIndex - 1 : items.length - 1;
}
if ([KEY_CODE_ARROW_DOWN, KEY_CODE_ARROW_UP].includes(keyCode) && items[updatedIndex]) {
var _pluginState$searchSe4;
_this.setState({
selectedIndex: updatedIndex,
displayUrl: items[updatedIndex].url
});
_this.fireAnalytics({
action: _analytics.ACTION.HIGHLIGHTED,
actionSubject: _analytics.ACTION_SUBJECT.SEARCH_RESULT,
attributes: {
source: _this.analyticSource,
searchSessionId: (_pluginState$searchSe4 = pluginState.searchSessionId) !== null && _pluginState$searchSe4 !== void 0 ? _pluginState$searchSe4 : '',
selectedResultId: items[updatedIndex].objectId,
selectedRelativePosition: updatedIndex
},
eventType: _analytics.EVENT_TYPE.UI
});
}
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "updateTextInput", function (displayText) {
_this.setState({
displayText: displayText
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "handleCancel", function (e) {
var _this$props4 = _this.props,
_this$props4$view = _this$props4.view,
state = _this$props4$view.state,
dispatch = _this$props4$view.dispatch,
onClickAwayCallback = _this$props4.onClickAwayCallback;
e.preventDefault();
onClickAwayCallback === null || onClickAwayCallback === void 0 || onClickAwayCallback(state, dispatch);
});
_this.state = {
selectedIndex: -1,
isLoading: false,
displayUrl: (0, _utils.normalizeUrl)(props.displayUrl),
displayText: props.displayText,
items: []
};
/* Cache functions */
_this.handleClearText = _this.createClearHandler('displayUrl');
_this.handleClearDisplayText = _this.createClearHandler('displayText');
_this.debouncedQuickSearch = (0, _debounce.default)(_this.quickSearch, 400);
_this.fireCustomAnalytics = (0, _analytics.fireAnalyticsEvent)(props.createAnalyticsEvent);
return _this;
}
(0, _createClass2.default)(HyperlinkLinkAddToolbar, [{
key: "componentDidMount",
value: function () {
var _componentDidMount = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4() {
var _pluginState$searchSe5,
_pluginState$inputMet,
_this2 = this;
var pluginState, _yield$Promise$all, _yield$Promise$all2, activityProvider, searchProvider;
return _regenerator.default.wrap(function _callee4$(_context4) {
while (1) switch (_context4.prev = _context4.next) {
case 0:
pluginState = this.props.pluginState;
document.addEventListener('mousedown', this.handleClickOutside);
this.fireAnalytics({
action: _analytics.ACTION.VIEWED,
actionSubject: _analytics.ACTION_SUBJECT.CREATE_LINK_INLINE_DIALOG,
attributes: {
timesViewed: pluginState.timesViewed,
searchSessionId: (_pluginState$searchSe5 = pluginState.searchSessionId) !== null && _pluginState$searchSe5 !== void 0 ? _pluginState$searchSe5 : '',
trigger: (_pluginState$inputMet = pluginState.inputMethod) !== null && _pluginState$inputMet !== void 0 ? _pluginState$inputMet : ''
},
eventType: _analytics.EVENT_TYPE.SCREEN
});
_context4.next = 5;
return Promise.all([this.props.activityProvider, this.props.searchProvider]);
case 5:
_yield$Promise$all = _context4.sent;
_yield$Promise$all2 = (0, _slicedToArray2.default)(_yield$Promise$all, 2);
activityProvider = _yield$Promise$all2[0];
searchProvider = _yield$Promise$all2[1];
// loadInitialLinkSearchResult and updateInput rely on activityProvider being set in state
(0, _reactDom.flushSync)(function () {
return _this2.setState({
activityProvider: activityProvider,
searchProvider: searchProvider
});
});
_context4.next = 12;
return this.loadInitialLinkSearchResult();
case 12:
case "end":
return _context4.stop();
}
}, _callee4, this);
}));
function componentDidMount() {
return _componentDidMount.apply(this, arguments);
}
return componentDidMount;
}()
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
var pluginState = this.props.pluginState;
document.removeEventListener('mousedown', this.handleClickOutside);
if (!this.submitted) {
var _pluginState$searchSe6;
this.fireAnalytics({
action: _analytics.ACTION.DISMISSED,
actionSubject: _analytics.ACTION_SUBJECT.CREATE_LINK_INLINE_DIALOG,
attributes: {
source: this.analyticSource,
searchSessionId: (_pluginState$searchSe6 = pluginState.searchSessionId) !== null && _pluginState$searchSe6 !== void 0 ? _pluginState$searchSe6 : '',
trigger: 'blur'
},
eventType: _analytics.EVENT_TYPE.UI
});
}
}
}, {
key: "getRecentItems",
value: function () {
var _getRecentItems = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5(activityProvider, query) {
var pluginState, perfStart, _pluginState$searchSe7, activityRecentItems, _items2, perfStop, duration, _perfStop2, _duration2;
return _regenerator.default.wrap(function _callee5$(_context5) {
while (1) switch (_context5.prev = _context5.next) {
case 0:
pluginState = this.props.pluginState;
perfStart = performance.now();
_context5.prev = 2;
_context5.t0 = limit;
if (!query) {
_context5.next = 10;
break;
}
_context5.next = 7;
return activityProvider.searchRecent(query);
case 7:
_context5.t1 = _context5.sent;
_context5.next = 13;
break;
case 10:
_context5.next = 12;
return activityProvider.getRecentItems();
case 12:
_context5.t1 = _context5.sent;
case 13:
_context5.t2 = _context5.t1;
activityRecentItems = (0, _context5.t0)(_context5.t2);
_items2 = activityRecentItems.map(mapActivityProviderResultToLinkSearchItemData);
perfStop = performance.now();
duration = perfStop - perfStart;
this.fireAnalytics({
action: _analytics.ACTION.INVOKED,
actionSubject: _analytics.ACTION_SUBJECT.SEARCH_RESULT,
actionSubjectId: _analytics.ACTION_SUBJECT_ID.RECENT_ACTIVITIES,
attributes: {
duration: duration,
count: _items2.length
},
eventType: _analytics.EVENT_TYPE.OPERATIONAL
});
this.fireAnalytics({
action: _analytics.ACTION.SHOWN,
actionSubject: _analytics.ACTION_SUBJECT.SEARCH_RESULT,
actionSubjectId: _analytics.ACTION_SUBJECT_ID.PRE_QUERY_SEARCH_RESULTS,
attributes: {
source: this.analyticSource,
preQueryRequestDurationMs: duration,
searchSessionId: (_pluginState$searchSe7 = pluginState.searchSessionId) !== null && _pluginState$searchSe7 !== void 0 ? _pluginState$searchSe7 : '',
resultCount: _items2.length,
results: activityRecentItems.map(function (item) {
var _item$type;
return {
resultContentId: item.objectId,
resultType: (_item$type = item.type) !== null && _item$type !== void 0 ? _item$type : ''
};
})
},
eventType: _analytics.EVENT_TYPE.UI
});
return _context5.abrupt("return", _items2);
case 23:
_context5.prev = 23;
_context5.t3 = _context5["catch"](2);
_perfStop2 = performance.now();
_duration2 = _perfStop2 - perfStart;
this.fireAnalytics({
action: _analytics.ACTION.INVOKED,
actionSubject: _analytics.ACTION_SUBJECT.SEARCH_RESULT,
actionSubjectId: _analytics.ACTION_SUBJECT_ID.RECENT_ACTIVITIES,
attributes: {
duration: _duration2,
count: -1,
errorCode: _context5.t3.status
},
eventType: _analytics.EVENT_TYPE.OPERATIONAL
});
return _context5.abrupt("return", []);
case 29:
case "end":
return _context5.stop();
}
}, _callee5, this, [[2, 23]]);
}));
function getRecentItems(_x5, _x6) {
return _getRecentItems.apply(this, arguments);
}
return getRecentItems;
}()
}, {
key: "fireAnalytics",
value: function fireAnalytics(payload) {
if (this.props.createAnalyticsEvent && this.fireCustomAnalytics) {
this.fireCustomAnalytics({
payload: payload
});
}
}
}, {
key: "loadInitialLinkSearchResult",
value: function () {
var _loadInitialLinkSearchResult = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee6() {
var _this$state10, displayUrl, activityProvider, _items3;
return _regenerator.default.wrap(function _callee6$(_context6) {
while (1) switch (_context6.prev = _context6.next) {
case 0:
_this$state10 = this.state, displayUrl = _this$state10.displayUrl, activityProvider = _this$state10.activityProvider;
_context6.prev = 1;
if (!(!displayUrl && activityProvider)) {
_context6.next = 8;
break;
}
this.setState({
isLoading: true
});
_context6.next = 6;
return this.getRecentItems(activityProvider);
case 6:
_items3 = _context6.sent;
this.setState({
items: _items3
});
case 8:
_context6.prev = 8;
this.setState({
isLoading: false
});
return _context6.finish(8);
case 11:
case "end":
return _context6.stop();
}
}, _callee6, this, [[1,, 8, 11]]);
}));
function loadInitialLinkSearchResult() {
return _loadInitialLinkSearchResult.apply(this, arguments);
}
return loadInitialLinkSearchResult;
}()
}, {
key: "render",
value: function render() {
var _this3 = this;
var _this$state11 = this.state,
items = _this$state11.items,
isLoading = _this$state11.isLoading,
selectedIndex = _this$state11.selectedIndex,
displayUrl = _this$state11.displayUrl,
displayText = _this$state11.displayText;
var _this$props5 = this.props,
formatMessage = _this$props5.intl.formatMessage,
activityProvider = _this$props5.activityProvider;
var formatClearLinkText = formatMessage(messages.clearLink);
var screenReaderDescriptionId = 'search-recent-links-field-description';
var linkSearchListId = 'hyperlink-search-list';
var ariaActiveDescendant = selectedIndex > -1 ? "link-search-list-item-".concat(selectedIndex) : '';
var linkSearchInputId = 'search-recent-links-field-id';
var displayTextInputId = 'display-text-filed-id';
// Added workaround with a screen reader Announcer specifically for VoiceOver + Safari
// as the Aria design pattern for combobox does not work in this case
// for details: https://a11y-internal.atlassian.net/browse/AK-740
var screenReaderText = _utils.browser.safari && this.getScreenReaderText();
return (0, _react2.jsx)("div", {
"aria-label": "Hyperlink Edit",
className: "recent-list",
"data-testid": "hyperlink-add-toolbar"
}, (0, _react2.jsx)("div", {
css: [_ToolbarComponents.container, !!activityProvider && _ToolbarComponents.containerWithProvider, containerPadding],
ref: this.wrapperRef
}, (0, _react2.jsx)("label", {
htmlFor: linkSearchInputId,
css: inputLabel
}, formatMessage(messages.linkVisibleLabel)), (0, _react2.jsx)("div", {
css: [_ToolbarComponents.inputWrapper, inputWrapperPosition]
}, screenReaderText && (0, _react2.jsx)(_ui.Announcer, {
ariaLive: "assertive",
text: screenReaderText,
ariaRelevant: "additions",
delay: 250
}), (0, _react2.jsx)("div", {
css: visuallyHiddenStyles,
"aria-hidden": "true",
id: screenReaderDescriptionId
}, formatMessage(messages.searchLinkAriaDescription)), (0, _react2.jsx)(_ui.PanelTextInput, {
role: "combobox",
ariaExpanded: true,
ariaActiveDescendant: ariaActiveDescendant,
ariaControls: linkSearchListId,
ariaAutoComplete: true,
describedById: screenReaderDescriptionId,
ref: function ref(ele) {
return _this3.urlInputContainer = ele;
},
testId: 'link-url',
onSubmit: this.handleSubmit,
onChange: this.updateInput,
autoFocus: {
preventScroll: true
},
onCancel: this.handleCancel,
defaultValue: displayUrl,
onKeyDown: this.handleKeyDown,
inputId: linkSearchInputId
}), displayUrl && (0, _react2.jsx)("div", {
css: clearTextWrapper
}, (0, _react2.jsx)(_tooltip.default, {
content: formatClearLinkText
}, (0, _react2.jsx)("button", {
type: "button",
css: clearText,
onClick: this.handleClearText,
tabIndex: 0
}, (0, _react2.jsx)(_crossCircle.default, {
label: formatClearLinkText
}))))), (0, _react2.jsx)("label", {
htmlFor: displayTextInputId,
css: [inputLabel, textLabelMargin]
}, formatMessage(messages.textVisibleLabel)), (0, _react2.jsx)("div", {
css: [_ToolbarComponents.inputWrapper, inputWrapperPosition]
}, (0, _react2.jsx)(_ui.PanelTextInput, {
ref: function ref(ele) {
return _this3.displayTextInputContainer = ele;
},
testId: 'link-text',
onChange: this.updateTextInput,
onCancel: this.handleCancel,
defaultValue: displayText,
onSubmit: this.handleSubmit,
onKeyDown: this.handleKeyDown,
inputId: displayTextInputId
}), displayText && (0, _react2.jsx)("div", {
css: clearTextWrapper
}, (0, _react2.jsx)(_tooltip.default, {
content: formatMessage(messages.clearText)
}, (0, _react2.jsx)("button", {
type: "button",
css: clearText,
onClick: this.handleClearDisplayText,
onKeyDown: this.handleClearTextKeyDown
}, (0, _react2.jsx)(_crossCircle.default, {
label: formatMessage(messages.clearText)
}))))), (0, _react2.jsx)("div", {
css: visuallyHiddenStyles,
"aria-live": "polite",
"aria-atomic": "true",
id: "fabric.editor.hyperlink.suggested.results"
}, displayUrl && formatMessage(messages.searchLinkResults, {
count: items.length
})), (0, _react2.jsx)(_LinkSearchList.default, {
ariaControls: "fabric.editor.hyperlink.suggested.results",
id: linkSearchListId,
role: "listbox",
items: items,
isLoading: isLoading,
selectedIndex: selectedIndex,
onSelect: this.handleSelected,
onMouseEnter: this.handleMouseEnterResultItem,
onMouseLeave: this.handleMouseLeaveResultItem
})));
}
}]);
return HyperlinkLinkAddToolbar;
}(_react.PureComponent);
function findIndex(array, predicate) {
var index = -1;
array.some(function (item, i) {
if (predicate(item)) {
index = i;
return true;
}
return false;
});
return index;
}
function limit(items) {
return items.slice(0, RECENT_SEARCH_LIST_SIZE);
}
var HyperlinkLinkAddToolbarWithIntl = exports.HyperlinkLinkAddToolbarWithIntl = (0, _reactIntlNext.injectIntl)(HyperlinkLinkAddToolbar);
var _default = exports.default = (0, _analyticsNext.withAnalyticsEvents)()(HyperlinkLinkAddToolbarWithIntl);