UNPKG

@atlaskit/editor-plugin-floating-toolbar

Version:

Floating toolbar plugin for @atlaskit/editor-core

157 lines (153 loc) 7.14 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports.EmojiPickerButton = void 0; var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _react = _interopRequireWildcard(require("react")); var _react2 = require("@emotion/react"); var _new = require("@atlaskit/button/new"); var _hooks = require("@atlaskit/editor-common/hooks"); var _ui = require("@atlaskit/editor-common/ui"); var _uiReact = require("@atlaskit/editor-common/ui-react"); var _emoji = require("@atlaskit/emoji"); var _emojiAdd = _interopRequireDefault(require("@atlaskit/icon/core/emoji-add")); var _tooltip = _interopRequireDefault(require("@atlaskit/tooltip")); function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); } /** * @jsxRuntime classic * @jsx jsx */ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766 // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage var emojiPickerButtonWrapperVisualRefresh = (0, _react2.css)({ position: 'relative', // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors button: { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-selectors '&:not([disabled])::after': { border: 'none' // remove blue border when picker has been selected } } }); var selector = function selector(states) { var _states$emojiState; return (_states$emojiState = states.emojiState) === null || _states$emojiState === void 0 ? void 0 : _states$emojiState.emojiProvider; }; var EmojiPickerWithProvider = function EmojiPickerWithProvider(props) { var emojiProvider = (0, _hooks.useSharedPluginStateWithSelector)(props.pluginInjectionApi, ['emoji'], selector); var setOutsideClickTargetRef = (0, _react.useContext)(_uiReact.OutsideClickTargetRefContext); if (!emojiProvider) { return null; } return (0, _react2.jsx)(_emoji.EmojiPicker, { emojiProvider: Promise.resolve(emojiProvider), onSelection: props.updateEmoji, onPickerRef: setOutsideClickTargetRef }); }; // Note: These are based on the height and width of the emoji picker at the time // of writing (2025-05-05). It is 100% prone to change but at least it's vaguely // written down. var EMOJI_PICKER_MAX_HEIGHT = 431; var EMOJI_PICKER_MAX_WIDTH = 352; var EmojiPickerWithListener = (0, _uiReact.withReactEditorViewOuterListeners)(EmojiPickerWithProvider); var EmojiPickerButton = exports.EmojiPickerButton = function EmojiPickerButton(props) { var buttonRef = _react.default.useRef(null); var _React$useState = _react.default.useState(false), _React$useState2 = (0, _slicedToArray2.default)(_React$useState, 2), isPopupOpen = _React$useState2[0], setIsPopupOpen = _React$useState2[1]; _react.default.useEffect(function () { if (props.setDisableParentScroll) { props.setDisableParentScroll(isPopupOpen); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [isPopupOpen]); var togglePopup = (0, _react.useCallback)(function () { setIsPopupOpen(!isPopupOpen); }, [setIsPopupOpen, isPopupOpen]); var updateEmoji = function updateEmoji(emoji) { setIsPopupOpen(false); props.onChange && props.onChange(emoji); requestAnimationFrame(function () { var _props$editorView; (_props$editorView = props.editorView) === null || _props$editorView === void 0 || _props$editorView.focus(); }); }; var isDetachedElement = (0, _react.useCallback)(function (el) { return !document.body.contains(el); }, []); var handleEmojiClickOutside = (0, _react.useCallback)(function (e) { // Ignore click events for detached elements. // Workaround for CETI-240 - where two onClicks fire - one when the upload button is // still in the document, and one once it's detached. Does not always occur, and // may be a side effect of a react render optimisation // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting if (e && e.target && !isDetachedElement(e.target)) { togglePopup(); } }, [isDetachedElement, togglePopup]); var handleEmojiPressEscape = (0, _react.useCallback)(function () { var _buttonRef$current; setIsPopupOpen(false); (_buttonRef$current = buttonRef.current) === null || _buttonRef$current === void 0 || _buttonRef$current.focus(); }, [setIsPopupOpen, buttonRef]); var renderPopup = function renderPopup() { if (!buttonRef.current || !isPopupOpen) { return; } return (0, _react2.jsx)(_ui.Popup, { target: buttonRef.current // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion , mountTo: props.setDisableParentScroll ? props.mountPoint : buttonRef.current.parentElement, fitHeight: EMOJI_PICKER_MAX_HEIGHT, fitWidth: EMOJI_PICKER_MAX_WIDTH // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , offset: [0, 10] // Confluence inline comment editor has z-index: 500 // if the toolbar is scrollable, this will be mounted in the root editor // we need an index of > 500 to display over it , zIndex: props.setDisableParentScroll ? 600 : undefined, focusTrap: true, preventOverflow: true, boundariesElement: props.popupsBoundariesElement }, (0, _react2.jsx)(EmojiPickerWithListener, { handleEscapeKeydown: handleEmojiPressEscape, handleClickOutside: handleEmojiClickOutside, pluginInjectionApi: props.pluginInjectionApi, updateEmoji: updateEmoji })); }; var title = props.title || ''; return (0, _react2.jsx)("div", { css: emojiPickerButtonWrapperVisualRefresh }, (0, _react2.jsx)(_tooltip.default, { content: title, position: "top" }, (0, _react2.jsx)(_new.IconButton, { appearance: "subtle", key: props.idx, onClick: togglePopup, ref: buttonRef, isSelected: props.isSelected, label: title, spacing: "compact" // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , icon: function icon() { return (0, _react2.jsx)(_emojiAdd.default, { color: "currentColor", label: "emoji-picker-button", spacing: "spacious" }); } })), renderPopup()); };