@atlaskit/editor-plugin-emoji 3.1.3 → 3.2.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 CHANGED
@@ -1,5 +1,24 @@
1
1
  # @atlaskit/editor-plugin-emoji
2
2
 
3
+ ## 3.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#122920](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/122920)
8
+ [`18c265545150a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/18c265545150a) -
9
+ [ux] [ED-26914] Emoji quick insert will now open the emoji picker rather than the typeahead when
10
+ platform_editor_controls is enabled
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies
15
+
16
+ ## 3.1.4
17
+
18
+ ### Patch Changes
19
+
20
+ - Updated dependencies
21
+
3
22
  ## 3.1.3
4
23
 
5
24
  ### Patch Changes
@@ -28,11 +28,13 @@ var _state = require("@atlaskit/editor-prosemirror/state");
28
28
  var _emoji2 = require("@atlaskit/emoji");
29
29
  var _comment = _interopRequireDefault(require("@atlaskit/icon/core/comment"));
30
30
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
31
+ var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
31
32
  var _insertEmoji = require("./editor-commands/insert-emoji");
32
33
  var _emoji3 = require("./nodeviews/emoji");
33
34
  var _emojiNodeSpec = require("./nodeviews/emojiNodeSpec");
34
35
  var _actions = require("./pm-plugins/actions");
35
36
  var _asciiInputRules = require("./pm-plugins/ascii-input-rules");
37
+ var _InlineEmojiPopup = require("./ui/InlineEmojiPopup");
36
38
  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); }
37
39
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
38
40
  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; }
@@ -264,12 +266,14 @@ var emojiPlugin = exports.emojiPlugin = function emojiPlugin(_ref2) {
264
266
  var _ref9 = (_emojiPluginKey$getSt = emojiPluginKey.getState(editorState)) !== null && _emojiPluginKey$getSt !== void 0 ? _emojiPluginKey$getSt : {},
265
267
  emojiResourceConfig = _ref9.emojiResourceConfig,
266
268
  asciiMap = _ref9.asciiMap,
267
- emojiProvider = _ref9.emojiProvider;
269
+ emojiProvider = _ref9.emojiProvider,
270
+ inlineEmojiPopupOpen = _ref9.inlineEmojiPopupOpen;
268
271
  return {
269
272
  emojiResourceConfig: emojiResourceConfig,
270
273
  asciiMap: asciiMap,
271
274
  typeAheadHandler: typeAhead,
272
- emojiProvider: emojiProvider
275
+ emojiProvider: emojiProvider,
276
+ inlineEmojiPopupOpen: inlineEmojiPopupOpen
273
277
  };
274
278
  },
275
279
  actions: {
@@ -311,9 +315,28 @@ var emojiPlugin = exports.emojiPlugin = function emojiPlugin(_ref2) {
311
315
  commands: {
312
316
  insertEmoji: (0, _insertEmoji.insertEmoji)(api === null || api === void 0 || (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions)
313
317
  },
318
+ contentComponent: function contentComponent(_ref11) {
319
+ var editorView = _ref11.editorView,
320
+ popupsBoundariesElement = _ref11.popupsBoundariesElement,
321
+ popupsMountPoint = _ref11.popupsMountPoint,
322
+ popupsScrollableElement = _ref11.popupsScrollableElement;
323
+ if (!api || (0, _experiments.editorExperiment)('platform_editor_controls', 'control')) {
324
+ return null;
325
+ }
326
+ return /*#__PURE__*/_react.default.createElement(_InlineEmojiPopup.InlineEmojiPopup, {
327
+ api: api,
328
+ editorView: editorView,
329
+ popupsBoundariesElement: popupsBoundariesElement,
330
+ popupsMountPoint: popupsMountPoint,
331
+ popupsScrollableElement: popupsScrollableElement,
332
+ onClose: function onClose() {
333
+ editorView.dispatch((0, _actions.setInlineEmojiPopupOpen)(false)(editorView.state.tr));
334
+ }
335
+ });
336
+ },
314
337
  pluginsOptions: {
315
- quickInsert: function quickInsert(_ref11) {
316
- var formatMessage = _ref11.formatMessage;
338
+ quickInsert: function quickInsert(_ref12) {
339
+ var formatMessage = _ref12.formatMessage;
317
340
  return [{
318
341
  id: 'emoji',
319
342
  title: formatMessage(_messages.toolbarInsertBlockMessages.emoji),
@@ -324,8 +347,16 @@ var emojiPlugin = exports.emojiPlugin = function emojiPlugin(_ref2) {
324
347
  icon: function icon() {
325
348
  return /*#__PURE__*/_react.default.createElement(_quickInsert.IconEmoji, null);
326
349
  },
327
- action: function action(insert, state) {
350
+ action: function action(insert) {
328
351
  var _api$typeAhead;
352
+ if ((0, _experiments.editorExperiment)('platform_editor_controls', 'variant1', {
353
+ exposure: true
354
+ })) {
355
+ // Clear slash
356
+ var _tr = insert('');
357
+ _tr = (0, _actions.setInlineEmojiPopupOpen)(true)(_tr);
358
+ return _tr;
359
+ }
329
360
  var tr = insert(undefined);
330
361
  api === null || api === void 0 || (_api$typeAhead = api.typeAhead) === null || _api$typeAhead === void 0 || _api$typeAhead.actions.openAtTransaction({
331
362
  triggerHandler: typeAhead,
@@ -479,12 +510,12 @@ function createEmojiPlugin(pmPluginFactoryParams, options, api) {
479
510
  return {};
480
511
  },
481
512
  apply: function apply(tr, pluginState) {
482
- var _ref12 = tr.getMeta(emojiPluginKey) || {
513
+ var _ref13 = tr.getMeta(emojiPluginKey) || {
483
514
  action: null,
484
515
  params: null
485
516
  },
486
- action = _ref12.action,
487
- params = _ref12.params;
517
+ action = _ref13.action,
518
+ params = _ref13.params;
488
519
  var newPluginState = pluginState;
489
520
  switch (action) {
490
521
  case _actions.ACTIONS.SET_PROVIDER:
@@ -499,6 +530,12 @@ function createEmojiPlugin(pmPluginFactoryParams, options, api) {
499
530
  });
500
531
  pmPluginFactoryParams.dispatch(emojiPluginKey, newPluginState);
501
532
  return newPluginState;
533
+ case _actions.ACTIONS.SET_INLINE_POPUP:
534
+ newPluginState = _objectSpread(_objectSpread({}, pluginState), {}, {
535
+ inlineEmojiPopupOpen: params.open
536
+ });
537
+ pmPluginFactoryParams.dispatch(emojiPluginKey, newPluginState);
538
+ return newPluginState;
502
539
  }
503
540
  return newPluginState;
504
541
  }
@@ -3,12 +3,13 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.setProvider = exports.setAsciiMap = exports.openTypeAhead = exports.ACTIONS = void 0;
6
+ exports.setProvider = exports.setInlineEmojiPopupOpen = exports.setAsciiMap = exports.openTypeAhead = exports.ACTIONS = void 0;
7
7
  var _emojiPlugin = require("../emojiPlugin");
8
8
  var ACTIONS = exports.ACTIONS = {
9
9
  SET_PROVIDER: 'SET_PROVIDER',
10
10
  SET_RESULTS: 'SET_RESULTS',
11
- SET_ASCII_MAP: 'SET_ASCII_MAP'
11
+ SET_ASCII_MAP: 'SET_ASCII_MAP',
12
+ SET_INLINE_POPUP: 'SET_INLINE_POPUP'
12
13
  };
13
14
  var setAsciiMap = exports.setAsciiMap = function setAsciiMap(asciiMap) {
14
15
  return function (tr) {
@@ -38,4 +39,14 @@ var setProvider = exports.setProvider = function setProvider(provider) {
38
39
  }
39
40
  });
40
41
  };
42
+ };
43
+ var setInlineEmojiPopupOpen = exports.setInlineEmojiPopupOpen = function setInlineEmojiPopupOpen(open) {
44
+ return function (tr) {
45
+ return tr.setMeta(_emojiPlugin.emojiPluginKey, {
46
+ action: ACTIONS.SET_INLINE_POPUP,
47
+ params: {
48
+ open: open
49
+ }
50
+ });
51
+ };
41
52
  };
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+
3
+ var _typeof = require("@babel/runtime/helpers/typeof");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.InlineEmojiPopup = void 0;
8
+ var _react = _interopRequireWildcard(require("react"));
9
+ var _reactIntlNext = require("react-intl-next");
10
+ var _analytics = require("@atlaskit/editor-common/analytics");
11
+ var _hooks = require("@atlaskit/editor-common/hooks");
12
+ var _ui = require("@atlaskit/editor-common/ui");
13
+ var _uiReact = require("@atlaskit/editor-common/ui-react");
14
+ var _utils = require("@atlaskit/editor-prosemirror/utils");
15
+ var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
16
+ var _emoji = require("@atlaskit/emoji");
17
+ 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); }
18
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
19
+ var PopupWithListeners = (0, _uiReact.withReactEditorViewOuterListeners)(_ui.Popup);
20
+ var getDomRefFromSelection = function getDomRefFromSelection(view) {
21
+ var domRef = (0, _utils.findDomRefAtPos)(view.state.selection.from, view.domAtPos.bind(view));
22
+ if (domRef instanceof HTMLElement) {
23
+ if (domRef.nodeName !== 'P') {
24
+ var paragraphRef = domRef.closest('p');
25
+ if (paragraphRef) {
26
+ return paragraphRef;
27
+ }
28
+ }
29
+ return domRef;
30
+ }
31
+ throw new Error('Invalid DOM reference');
32
+ };
33
+ var emojiPopupMessages = {
34
+ emojiPickerAriaLabel: {
35
+ id: 'fabric.emoji.picker.aria.label',
36
+ defaultMessage: 'Emoji picker',
37
+ description: 'Accessible label for the emoji picker popup'
38
+ }
39
+ };
40
+ var InlineEmojiPopup = exports.InlineEmojiPopup = function InlineEmojiPopup(_ref) {
41
+ var _useSharedPluginState, _useSharedPluginState2;
42
+ var api = _ref.api,
43
+ popupsMountPoint = _ref.popupsMountPoint,
44
+ popupsBoundariesElement = _ref.popupsBoundariesElement,
45
+ popupsScrollableElement = _ref.popupsScrollableElement,
46
+ editorView = _ref.editorView,
47
+ onClose = _ref.onClose;
48
+ var _ref2 = (_useSharedPluginState = (_useSharedPluginState2 = (0, _hooks.useSharedPluginState)(api, ['emoji'])) === null || _useSharedPluginState2 === void 0 ? void 0 : _useSharedPluginState2.emojiState) !== null && _useSharedPluginState !== void 0 ? _useSharedPluginState : {},
49
+ emojiProvider = _ref2.emojiProvider,
50
+ isOpen = _ref2.inlineEmojiPopupOpen;
51
+ var intl = (0, _reactIntlNext.useIntl)();
52
+ var focusEditor = (0, _react.useCallback)(function () {
53
+ // use requestAnimationFrame to run this async after the call
54
+ requestAnimationFrame(function () {
55
+ return editorView.focus();
56
+ });
57
+ }, [editorView]);
58
+ var handleSelection = (0, _react.useCallback)(function (emojiId) {
59
+ api.core.actions.execute(api.emoji.commands.insertEmoji(emojiId, _analytics.INPUT_METHOD.PICKER));
60
+ onClose();
61
+ }, [api.core.actions, api.emoji.commands, onClose]);
62
+ if (!isOpen || !emojiProvider) {
63
+ return null;
64
+ }
65
+ var domRef = getDomRefFromSelection(editorView);
66
+ return /*#__PURE__*/_react.default.createElement(PopupWithListeners, {
67
+ ariaLabel: intl.formatMessage(emojiPopupMessages.emojiPickerAriaLabel),
68
+ offset: [0, 12],
69
+ mountTo: popupsMountPoint,
70
+ boundariesElement: popupsBoundariesElement,
71
+ scrollableElement: popupsScrollableElement,
72
+ zIndex: _editorSharedStyles.akEditorFloatingDialogZIndex,
73
+ fitHeight: 350,
74
+ fitWidth: 350,
75
+ target: domRef,
76
+ onUnmount: focusEditor,
77
+ focusTrap: true,
78
+ preventOverflow: true,
79
+ handleClickOutside: onClose,
80
+ handleEscapeKeydown: onClose,
81
+ captureClick: true
82
+ }, /*#__PURE__*/_react.default.createElement(_uiReact.OutsideClickTargetRefContext.Consumer, null, function (setOutsideClickTargetRef) {
83
+ return /*#__PURE__*/_react.default.createElement(_emoji.EmojiPicker, {
84
+ emojiProvider: Promise.resolve(emojiProvider),
85
+ onPickerRef: setOutsideClickTargetRef,
86
+ onSelection: handleSelection
87
+ });
88
+ }));
89
+ };
@@ -12,11 +12,13 @@ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
12
12
  import { EmojiTypeAheadItem, preloadEmojiPicker, recordSelectionFailedSli, recordSelectionSucceededSli, SearchSort } from '@atlaskit/emoji';
13
13
  import CommentIcon from '@atlaskit/icon/core/comment';
14
14
  import { fg } from '@atlaskit/platform-feature-flags';
15
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
15
16
  import { createEmojiFragment, insertEmoji } from './editor-commands/insert-emoji';
16
17
  import { EmojiNodeView } from './nodeviews/emoji';
17
18
  import { emojiNodeSpec } from './nodeviews/emojiNodeSpec';
18
- import { ACTIONS, openTypeAhead as openTypeAheadAction, setAsciiMap as setAsciiMapAction, setProvider as setProviderAction } from './pm-plugins/actions';
19
+ import { ACTIONS, openTypeAhead as openTypeAheadAction, setAsciiMap as setAsciiMapAction, setInlineEmojiPopupOpen, setProvider as setProviderAction } from './pm-plugins/actions';
19
20
  import { inputRulePlugin as asciiInputRulePlugin } from './pm-plugins/ascii-input-rules';
21
+ import { InlineEmojiPopup } from './ui/InlineEmojiPopup';
20
22
  export const emojiToTypeaheadItem = (emoji, emojiProvider) => ({
21
23
  title: emoji.shortName || '',
22
24
  key: emoji.id || emoji.shortName,
@@ -246,13 +248,15 @@ export const emojiPlugin = ({
246
248
  const {
247
249
  emojiResourceConfig,
248
250
  asciiMap,
249
- emojiProvider
251
+ emojiProvider,
252
+ inlineEmojiPopupOpen
250
253
  } = (_emojiPluginKey$getSt = emojiPluginKey.getState(editorState)) !== null && _emojiPluginKey$getSt !== void 0 ? _emojiPluginKey$getSt : {};
251
254
  return {
252
255
  emojiResourceConfig,
253
256
  asciiMap,
254
257
  typeAheadHandler: typeAhead,
255
- emojiProvider
258
+ emojiProvider,
259
+ inlineEmojiPopupOpen
256
260
  };
257
261
  },
258
262
  actions: {
@@ -273,6 +277,26 @@ export const emojiPlugin = ({
273
277
  commands: {
274
278
  insertEmoji: insertEmoji(api === null || api === void 0 ? void 0 : (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions)
275
279
  },
280
+ contentComponent({
281
+ editorView,
282
+ popupsBoundariesElement,
283
+ popupsMountPoint,
284
+ popupsScrollableElement
285
+ }) {
286
+ if (!api || editorExperiment('platform_editor_controls', 'control')) {
287
+ return null;
288
+ }
289
+ return /*#__PURE__*/React.createElement(InlineEmojiPopup, {
290
+ api: api,
291
+ editorView: editorView,
292
+ popupsBoundariesElement: popupsBoundariesElement,
293
+ popupsMountPoint: popupsMountPoint,
294
+ popupsScrollableElement: popupsScrollableElement,
295
+ onClose: () => {
296
+ editorView.dispatch(setInlineEmojiPopupOpen(false)(editorView.state.tr));
297
+ }
298
+ });
299
+ },
276
300
  pluginsOptions: {
277
301
  quickInsert: ({
278
302
  formatMessage
@@ -284,8 +308,16 @@ export const emojiPlugin = ({
284
308
  keyshortcut: ':',
285
309
  isDisabledOffline: fg('platform_editor_preload_emoji_picker') ? false : true,
286
310
  icon: () => /*#__PURE__*/React.createElement(IconEmoji, null),
287
- action(insert, state) {
311
+ action(insert) {
288
312
  var _api$typeAhead;
313
+ if (editorExperiment('platform_editor_controls', 'variant1', {
314
+ exposure: true
315
+ })) {
316
+ // Clear slash
317
+ let tr = insert('');
318
+ tr = setInlineEmojiPopupOpen(true)(tr);
319
+ return tr;
320
+ }
289
321
  const tr = insert(undefined);
290
322
  api === null || api === void 0 ? void 0 : (_api$typeAhead = api.typeAhead) === null || _api$typeAhead === void 0 ? void 0 : _api$typeAhead.actions.openAtTransaction({
291
323
  triggerHandler: typeAhead,
@@ -451,6 +483,13 @@ export function createEmojiPlugin(pmPluginFactoryParams, options, api) {
451
483
  };
452
484
  pmPluginFactoryParams.dispatch(emojiPluginKey, newPluginState);
453
485
  return newPluginState;
486
+ case ACTIONS.SET_INLINE_POPUP:
487
+ newPluginState = {
488
+ ...pluginState,
489
+ inlineEmojiPopupOpen: params.open
490
+ };
491
+ pmPluginFactoryParams.dispatch(emojiPluginKey, newPluginState);
492
+ return newPluginState;
454
493
  }
455
494
  return newPluginState;
456
495
  }
@@ -2,7 +2,8 @@ import { emojiPluginKey } from '../emojiPlugin';
2
2
  export const ACTIONS = {
3
3
  SET_PROVIDER: 'SET_PROVIDER',
4
4
  SET_RESULTS: 'SET_RESULTS',
5
- SET_ASCII_MAP: 'SET_ASCII_MAP'
5
+ SET_ASCII_MAP: 'SET_ASCII_MAP',
6
+ SET_INLINE_POPUP: 'SET_INLINE_POPUP'
6
7
  };
7
8
  export const setAsciiMap = asciiMap => tr => {
8
9
  return tr.setMeta(emojiPluginKey, {
@@ -26,4 +27,10 @@ export const setProvider = provider => tr => {
26
27
  provider
27
28
  }
28
29
  });
29
- };
30
+ };
31
+ export const setInlineEmojiPopupOpen = open => tr => tr.setMeta(emojiPluginKey, {
32
+ action: ACTIONS.SET_INLINE_POPUP,
33
+ params: {
34
+ open
35
+ }
36
+ });
@@ -0,0 +1,78 @@
1
+ import React, { useCallback } from 'react';
2
+ import { useIntl } from 'react-intl-next';
3
+ import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
4
+ import { useSharedPluginState } from '@atlaskit/editor-common/hooks';
5
+ import { Popup } from '@atlaskit/editor-common/ui';
6
+ import { OutsideClickTargetRefContext, withReactEditorViewOuterListeners as withOuterListeners } from '@atlaskit/editor-common/ui-react';
7
+ import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
8
+ import { akEditorFloatingDialogZIndex } from '@atlaskit/editor-shared-styles';
9
+ import { EmojiPicker } from '@atlaskit/emoji';
10
+ const PopupWithListeners = withOuterListeners(Popup);
11
+ const getDomRefFromSelection = view => {
12
+ const domRef = findDomRefAtPos(view.state.selection.from, view.domAtPos.bind(view));
13
+ if (domRef instanceof HTMLElement) {
14
+ if (domRef.nodeName !== 'P') {
15
+ const paragraphRef = domRef.closest('p');
16
+ if (paragraphRef) {
17
+ return paragraphRef;
18
+ }
19
+ }
20
+ return domRef;
21
+ }
22
+ throw new Error('Invalid DOM reference');
23
+ };
24
+ const emojiPopupMessages = {
25
+ emojiPickerAriaLabel: {
26
+ id: 'fabric.emoji.picker.aria.label',
27
+ defaultMessage: 'Emoji picker',
28
+ description: 'Accessible label for the emoji picker popup'
29
+ }
30
+ };
31
+ export const InlineEmojiPopup = ({
32
+ api,
33
+ popupsMountPoint,
34
+ popupsBoundariesElement,
35
+ popupsScrollableElement,
36
+ editorView,
37
+ onClose
38
+ }) => {
39
+ var _useSharedPluginState, _useSharedPluginState2;
40
+ const {
41
+ emojiProvider,
42
+ inlineEmojiPopupOpen: isOpen
43
+ } = (_useSharedPluginState = (_useSharedPluginState2 = useSharedPluginState(api, ['emoji'])) === null || _useSharedPluginState2 === void 0 ? void 0 : _useSharedPluginState2.emojiState) !== null && _useSharedPluginState !== void 0 ? _useSharedPluginState : {};
44
+ const intl = useIntl();
45
+ const focusEditor = useCallback(() => {
46
+ // use requestAnimationFrame to run this async after the call
47
+ requestAnimationFrame(() => editorView.focus());
48
+ }, [editorView]);
49
+ const handleSelection = useCallback(emojiId => {
50
+ api.core.actions.execute(api.emoji.commands.insertEmoji(emojiId, INPUT_METHOD.PICKER));
51
+ onClose();
52
+ }, [api.core.actions, api.emoji.commands, onClose]);
53
+ if (!isOpen || !emojiProvider) {
54
+ return null;
55
+ }
56
+ const domRef = getDomRefFromSelection(editorView);
57
+ return /*#__PURE__*/React.createElement(PopupWithListeners, {
58
+ ariaLabel: intl.formatMessage(emojiPopupMessages.emojiPickerAriaLabel),
59
+ offset: [0, 12],
60
+ mountTo: popupsMountPoint,
61
+ boundariesElement: popupsBoundariesElement,
62
+ scrollableElement: popupsScrollableElement,
63
+ zIndex: akEditorFloatingDialogZIndex,
64
+ fitHeight: 350,
65
+ fitWidth: 350,
66
+ target: domRef,
67
+ onUnmount: focusEditor,
68
+ focusTrap: true,
69
+ preventOverflow: true,
70
+ handleClickOutside: onClose,
71
+ handleEscapeKeydown: onClose,
72
+ captureClick: true
73
+ }, /*#__PURE__*/React.createElement(OutsideClickTargetRefContext.Consumer, null, setOutsideClickTargetRef => /*#__PURE__*/React.createElement(EmojiPicker, {
74
+ emojiProvider: Promise.resolve(emojiProvider),
75
+ onPickerRef: setOutsideClickTargetRef,
76
+ onSelection: handleSelection
77
+ })));
78
+ };
@@ -17,11 +17,13 @@ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
17
17
  import { EmojiTypeAheadItem, preloadEmojiPicker, recordSelectionFailedSli, recordSelectionSucceededSli, SearchSort } from '@atlaskit/emoji';
18
18
  import CommentIcon from '@atlaskit/icon/core/comment';
19
19
  import { fg } from '@atlaskit/platform-feature-flags';
20
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
20
21
  import { createEmojiFragment, insertEmoji } from './editor-commands/insert-emoji';
21
22
  import { EmojiNodeView } from './nodeviews/emoji';
22
23
  import { emojiNodeSpec } from './nodeviews/emojiNodeSpec';
23
- import { ACTIONS, openTypeAhead as openTypeAheadAction, setAsciiMap as setAsciiMapAction, setProvider as setProviderAction } from './pm-plugins/actions';
24
+ import { ACTIONS, openTypeAhead as openTypeAheadAction, setAsciiMap as setAsciiMapAction, setInlineEmojiPopupOpen, setProvider as setProviderAction } from './pm-plugins/actions';
24
25
  import { inputRulePlugin as asciiInputRulePlugin } from './pm-plugins/ascii-input-rules';
26
+ import { InlineEmojiPopup } from './ui/InlineEmojiPopup';
25
27
  export var emojiToTypeaheadItem = function emojiToTypeaheadItem(emoji, emojiProvider) {
26
28
  return {
27
29
  title: emoji.shortName || '',
@@ -249,12 +251,14 @@ export var emojiPlugin = function emojiPlugin(_ref2) {
249
251
  var _ref9 = (_emojiPluginKey$getSt = emojiPluginKey.getState(editorState)) !== null && _emojiPluginKey$getSt !== void 0 ? _emojiPluginKey$getSt : {},
250
252
  emojiResourceConfig = _ref9.emojiResourceConfig,
251
253
  asciiMap = _ref9.asciiMap,
252
- emojiProvider = _ref9.emojiProvider;
254
+ emojiProvider = _ref9.emojiProvider,
255
+ inlineEmojiPopupOpen = _ref9.inlineEmojiPopupOpen;
253
256
  return {
254
257
  emojiResourceConfig: emojiResourceConfig,
255
258
  asciiMap: asciiMap,
256
259
  typeAheadHandler: typeAhead,
257
- emojiProvider: emojiProvider
260
+ emojiProvider: emojiProvider,
261
+ inlineEmojiPopupOpen: inlineEmojiPopupOpen
258
262
  };
259
263
  },
260
264
  actions: {
@@ -296,9 +300,28 @@ export var emojiPlugin = function emojiPlugin(_ref2) {
296
300
  commands: {
297
301
  insertEmoji: insertEmoji(api === null || api === void 0 || (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions)
298
302
  },
303
+ contentComponent: function contentComponent(_ref11) {
304
+ var editorView = _ref11.editorView,
305
+ popupsBoundariesElement = _ref11.popupsBoundariesElement,
306
+ popupsMountPoint = _ref11.popupsMountPoint,
307
+ popupsScrollableElement = _ref11.popupsScrollableElement;
308
+ if (!api || editorExperiment('platform_editor_controls', 'control')) {
309
+ return null;
310
+ }
311
+ return /*#__PURE__*/React.createElement(InlineEmojiPopup, {
312
+ api: api,
313
+ editorView: editorView,
314
+ popupsBoundariesElement: popupsBoundariesElement,
315
+ popupsMountPoint: popupsMountPoint,
316
+ popupsScrollableElement: popupsScrollableElement,
317
+ onClose: function onClose() {
318
+ editorView.dispatch(setInlineEmojiPopupOpen(false)(editorView.state.tr));
319
+ }
320
+ });
321
+ },
299
322
  pluginsOptions: {
300
- quickInsert: function quickInsert(_ref11) {
301
- var formatMessage = _ref11.formatMessage;
323
+ quickInsert: function quickInsert(_ref12) {
324
+ var formatMessage = _ref12.formatMessage;
302
325
  return [{
303
326
  id: 'emoji',
304
327
  title: formatMessage(messages.emoji),
@@ -309,8 +332,16 @@ export var emojiPlugin = function emojiPlugin(_ref2) {
309
332
  icon: function icon() {
310
333
  return /*#__PURE__*/React.createElement(IconEmoji, null);
311
334
  },
312
- action: function action(insert, state) {
335
+ action: function action(insert) {
313
336
  var _api$typeAhead;
337
+ if (editorExperiment('platform_editor_controls', 'variant1', {
338
+ exposure: true
339
+ })) {
340
+ // Clear slash
341
+ var _tr = insert('');
342
+ _tr = setInlineEmojiPopupOpen(true)(_tr);
343
+ return _tr;
344
+ }
314
345
  var tr = insert(undefined);
315
346
  api === null || api === void 0 || (_api$typeAhead = api.typeAhead) === null || _api$typeAhead === void 0 || _api$typeAhead.actions.openAtTransaction({
316
347
  triggerHandler: typeAhead,
@@ -464,12 +495,12 @@ export function createEmojiPlugin(pmPluginFactoryParams, options, api) {
464
495
  return {};
465
496
  },
466
497
  apply: function apply(tr, pluginState) {
467
- var _ref12 = tr.getMeta(emojiPluginKey) || {
498
+ var _ref13 = tr.getMeta(emojiPluginKey) || {
468
499
  action: null,
469
500
  params: null
470
501
  },
471
- action = _ref12.action,
472
- params = _ref12.params;
502
+ action = _ref13.action,
503
+ params = _ref13.params;
473
504
  var newPluginState = pluginState;
474
505
  switch (action) {
475
506
  case ACTIONS.SET_PROVIDER:
@@ -484,6 +515,12 @@ export function createEmojiPlugin(pmPluginFactoryParams, options, api) {
484
515
  });
485
516
  pmPluginFactoryParams.dispatch(emojiPluginKey, newPluginState);
486
517
  return newPluginState;
518
+ case ACTIONS.SET_INLINE_POPUP:
519
+ newPluginState = _objectSpread(_objectSpread({}, pluginState), {}, {
520
+ inlineEmojiPopupOpen: params.open
521
+ });
522
+ pmPluginFactoryParams.dispatch(emojiPluginKey, newPluginState);
523
+ return newPluginState;
487
524
  }
488
525
  return newPluginState;
489
526
  }
@@ -2,7 +2,8 @@ import { emojiPluginKey } from '../emojiPlugin';
2
2
  export var ACTIONS = {
3
3
  SET_PROVIDER: 'SET_PROVIDER',
4
4
  SET_RESULTS: 'SET_RESULTS',
5
- SET_ASCII_MAP: 'SET_ASCII_MAP'
5
+ SET_ASCII_MAP: 'SET_ASCII_MAP',
6
+ SET_INLINE_POPUP: 'SET_INLINE_POPUP'
6
7
  };
7
8
  export var setAsciiMap = function setAsciiMap(asciiMap) {
8
9
  return function (tr) {
@@ -32,4 +33,14 @@ export var setProvider = function setProvider(provider) {
32
33
  }
33
34
  });
34
35
  };
36
+ };
37
+ export var setInlineEmojiPopupOpen = function setInlineEmojiPopupOpen(open) {
38
+ return function (tr) {
39
+ return tr.setMeta(emojiPluginKey, {
40
+ action: ACTIONS.SET_INLINE_POPUP,
41
+ params: {
42
+ open: open
43
+ }
44
+ });
45
+ };
35
46
  };
@@ -0,0 +1,80 @@
1
+ import React, { useCallback } from 'react';
2
+ import { useIntl } from 'react-intl-next';
3
+ import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
4
+ import { useSharedPluginState } from '@atlaskit/editor-common/hooks';
5
+ import { Popup } from '@atlaskit/editor-common/ui';
6
+ import { OutsideClickTargetRefContext, withReactEditorViewOuterListeners as withOuterListeners } from '@atlaskit/editor-common/ui-react';
7
+ import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
8
+ import { akEditorFloatingDialogZIndex } from '@atlaskit/editor-shared-styles';
9
+ import { EmojiPicker } from '@atlaskit/emoji';
10
+ var PopupWithListeners = withOuterListeners(Popup);
11
+ var getDomRefFromSelection = function getDomRefFromSelection(view) {
12
+ var domRef = findDomRefAtPos(view.state.selection.from, view.domAtPos.bind(view));
13
+ if (domRef instanceof HTMLElement) {
14
+ if (domRef.nodeName !== 'P') {
15
+ var paragraphRef = domRef.closest('p');
16
+ if (paragraphRef) {
17
+ return paragraphRef;
18
+ }
19
+ }
20
+ return domRef;
21
+ }
22
+ throw new Error('Invalid DOM reference');
23
+ };
24
+ var emojiPopupMessages = {
25
+ emojiPickerAriaLabel: {
26
+ id: 'fabric.emoji.picker.aria.label',
27
+ defaultMessage: 'Emoji picker',
28
+ description: 'Accessible label for the emoji picker popup'
29
+ }
30
+ };
31
+ export var InlineEmojiPopup = function InlineEmojiPopup(_ref) {
32
+ var _useSharedPluginState, _useSharedPluginState2;
33
+ var api = _ref.api,
34
+ popupsMountPoint = _ref.popupsMountPoint,
35
+ popupsBoundariesElement = _ref.popupsBoundariesElement,
36
+ popupsScrollableElement = _ref.popupsScrollableElement,
37
+ editorView = _ref.editorView,
38
+ onClose = _ref.onClose;
39
+ var _ref2 = (_useSharedPluginState = (_useSharedPluginState2 = useSharedPluginState(api, ['emoji'])) === null || _useSharedPluginState2 === void 0 ? void 0 : _useSharedPluginState2.emojiState) !== null && _useSharedPluginState !== void 0 ? _useSharedPluginState : {},
40
+ emojiProvider = _ref2.emojiProvider,
41
+ isOpen = _ref2.inlineEmojiPopupOpen;
42
+ var intl = useIntl();
43
+ var focusEditor = useCallback(function () {
44
+ // use requestAnimationFrame to run this async after the call
45
+ requestAnimationFrame(function () {
46
+ return editorView.focus();
47
+ });
48
+ }, [editorView]);
49
+ var handleSelection = useCallback(function (emojiId) {
50
+ api.core.actions.execute(api.emoji.commands.insertEmoji(emojiId, INPUT_METHOD.PICKER));
51
+ onClose();
52
+ }, [api.core.actions, api.emoji.commands, onClose]);
53
+ if (!isOpen || !emojiProvider) {
54
+ return null;
55
+ }
56
+ var domRef = getDomRefFromSelection(editorView);
57
+ return /*#__PURE__*/React.createElement(PopupWithListeners, {
58
+ ariaLabel: intl.formatMessage(emojiPopupMessages.emojiPickerAriaLabel),
59
+ offset: [0, 12],
60
+ mountTo: popupsMountPoint,
61
+ boundariesElement: popupsBoundariesElement,
62
+ scrollableElement: popupsScrollableElement,
63
+ zIndex: akEditorFloatingDialogZIndex,
64
+ fitHeight: 350,
65
+ fitWidth: 350,
66
+ target: domRef,
67
+ onUnmount: focusEditor,
68
+ focusTrap: true,
69
+ preventOverflow: true,
70
+ handleClickOutside: onClose,
71
+ handleEscapeKeydown: onClose,
72
+ captureClick: true
73
+ }, /*#__PURE__*/React.createElement(OutsideClickTargetRefContext.Consumer, null, function (setOutsideClickTargetRef) {
74
+ return /*#__PURE__*/React.createElement(EmojiPicker, {
75
+ emojiProvider: Promise.resolve(emojiProvider),
76
+ onPickerRef: setOutsideClickTargetRef,
77
+ onSelection: handleSelection
78
+ });
79
+ }));
80
+ };
@@ -34,6 +34,7 @@ export type EmojiPluginState = {
34
34
  emojiProvider?: EmojiProvider;
35
35
  emojiResourceConfig?: EmojiResourceConfig;
36
36
  asciiMap?: Map<string, EmojiDescription>;
37
+ inlineEmojiPopupOpen?: boolean;
37
38
  };
38
39
  export type EmojiPluginSharedState = EmojiPluginState & {
39
40
  typeAheadHandler: TypeAheadHandler;
@@ -7,7 +7,9 @@ export declare const ACTIONS: {
7
7
  SET_PROVIDER: string;
8
8
  SET_RESULTS: string;
9
9
  SET_ASCII_MAP: string;
10
+ SET_INLINE_POPUP: string;
10
11
  };
11
12
  export declare const setAsciiMap: (asciiMap: Map<string, EmojiDescription>) => (tr: Transaction) => Transaction;
12
13
  export declare const openTypeAhead: (typeaheadHandler: TypeAheadHandler, api?: PublicPluginAPI<EmojiPlugin>) => (inputMethod: TypeAheadInputMethod) => boolean;
13
14
  export declare const setProvider: (provider?: EmojiProvider) => (tr: Transaction) => Transaction;
15
+ export declare const setInlineEmojiPopupOpen: (open: boolean) => (tr: Transaction) => Transaction;
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import type { ExtractInjectionAPI, UiComponentFactoryParams } from '@atlaskit/editor-common/types';
3
+ import { EditorView } from '@atlaskit/editor-prosemirror/view';
4
+ import { type EmojiPlugin } from '../emojiPluginType';
5
+ type InlineEmojiPopupProps = Pick<UiComponentFactoryParams, 'popupsBoundariesElement' | 'popupsMountPoint' | 'popupsScrollableElement'> & {
6
+ api: ExtractInjectionAPI<EmojiPlugin>;
7
+ editorView: EditorView;
8
+ onClose: () => void;
9
+ };
10
+ export declare const InlineEmojiPopup: ({ api, popupsMountPoint, popupsBoundariesElement, popupsScrollableElement, editorView, onClose, }: InlineEmojiPopupProps) => React.JSX.Element | null;
11
+ export {};
@@ -34,6 +34,7 @@ export type EmojiPluginState = {
34
34
  emojiProvider?: EmojiProvider;
35
35
  emojiResourceConfig?: EmojiResourceConfig;
36
36
  asciiMap?: Map<string, EmojiDescription>;
37
+ inlineEmojiPopupOpen?: boolean;
37
38
  };
38
39
  export type EmojiPluginSharedState = EmojiPluginState & {
39
40
  typeAheadHandler: TypeAheadHandler;
@@ -7,7 +7,9 @@ export declare const ACTIONS: {
7
7
  SET_PROVIDER: string;
8
8
  SET_RESULTS: string;
9
9
  SET_ASCII_MAP: string;
10
+ SET_INLINE_POPUP: string;
10
11
  };
11
12
  export declare const setAsciiMap: (asciiMap: Map<string, EmojiDescription>) => (tr: Transaction) => Transaction;
12
13
  export declare const openTypeAhead: (typeaheadHandler: TypeAheadHandler, api?: PublicPluginAPI<EmojiPlugin>) => (inputMethod: TypeAheadInputMethod) => boolean;
13
14
  export declare const setProvider: (provider?: EmojiProvider) => (tr: Transaction) => Transaction;
15
+ export declare const setInlineEmojiPopupOpen: (open: boolean) => (tr: Transaction) => Transaction;
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import type { ExtractInjectionAPI, UiComponentFactoryParams } from '@atlaskit/editor-common/types';
3
+ import { EditorView } from '@atlaskit/editor-prosemirror/view';
4
+ import { type EmojiPlugin } from '../emojiPluginType';
5
+ type InlineEmojiPopupProps = Pick<UiComponentFactoryParams, 'popupsBoundariesElement' | 'popupsMountPoint' | 'popupsScrollableElement'> & {
6
+ api: ExtractInjectionAPI<EmojiPlugin>;
7
+ editorView: EditorView;
8
+ onClose: () => void;
9
+ };
10
+ export declare const InlineEmojiPopup: ({ api, popupsMountPoint, popupsBoundariesElement, popupsScrollableElement, editorView, onClose, }: InlineEmojiPopupProps) => React.JSX.Element | null;
11
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-emoji",
3
- "version": "3.1.3",
3
+ "version": "3.2.0",
4
4
  "description": "Emoji plugin for @atlaskit/editor-core",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -23,13 +23,14 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@atlaskit/adf-schema": "^47.6.0",
26
- "@atlaskit/editor-common": "^101.1.0",
26
+ "@atlaskit/editor-common": "^102.1.0",
27
27
  "@atlaskit/editor-plugin-analytics": "^2.1.0",
28
- "@atlaskit/editor-plugin-annotation": "2.1.1",
28
+ "@atlaskit/editor-plugin-annotation": "^2.1.0",
29
29
  "@atlaskit/editor-plugin-base": "^2.3.0",
30
30
  "@atlaskit/editor-plugin-editor-viewmode": "^3.0.0",
31
31
  "@atlaskit/editor-plugin-type-ahead": "^2.1.0",
32
32
  "@atlaskit/editor-prosemirror": "7.0.0",
33
+ "@atlaskit/editor-shared-styles": "^3.4.0",
33
34
  "@atlaskit/emoji": "^68.0.0",
34
35
  "@atlaskit/icon": "^24.1.0",
35
36
  "@atlaskit/node-data-provider": "^4.1.0",
@@ -48,12 +49,12 @@
48
49
  "react-dom": "^18.2.0"
49
50
  },
50
51
  "devDependencies": {
51
- "@af/visual-regression": "*",
52
+ "@af/visual-regression": "^1.3.0",
52
53
  "@atlaskit/editor-plugin-composition": "^1.3.0",
53
54
  "@atlaskit/editor-plugin-decorations": "^2.0.0",
54
- "@atlaskit/ssr": "*",
55
+ "@atlaskit/ssr": "^0.4.0",
55
56
  "@atlaskit/util-data-test": "^18.0.0",
56
- "@atlaskit/visual-regression": "*",
57
+ "@atlaskit/visual-regression": "^0.10.0",
57
58
  "@testing-library/react": "^13.4.0",
58
59
  "typescript": "~5.4.2",
59
60
  "wait-for-expect": "^1.2.0"