UNPKG

@atlaskit/editor-common

Version:

A package that contains common classes and components for editor and renderer

416 lines (410 loc) • 23.6 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports.DropdownMenuItem = DropdownMenuItem; exports.default = exports.DropdownMenuWithKeyboardNavigation = void 0; var _objectDestructuringEmpty2 = _interopRequireDefault(require("@babel/runtime/helpers/objectDestructuringEmpty")); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _taggedTemplateLiteral2 = _interopRequireDefault(require("@babel/runtime/helpers/taggedTemplateLiteral")); var _react = _interopRequireWildcard(require("react")); var _react2 = require("@emotion/react"); var _editorSharedStyles = require("@atlaskit/editor-shared-styles"); var _menu = require("@atlaskit/menu"); var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals"); var _experiments = require("@atlaskit/tmp-editor-statsig/experiments"); var _tooltip = _interopRequireDefault(require("@atlaskit/tooltip")); var _styles = require("../../styles"); var _ToolbarArrowKeyNavigationProvider = require("../../ui-menu/ToolbarArrowKeyNavigationProvider"); var _uiReact = require("../../ui-react"); var _DropList = _interopRequireDefault(require("../../ui/DropList")); var _Popup = _interopRequireDefault(require("../../ui/Popup")); var _ArrowKeyNavigationProvider = require("../ArrowKeyNavigationProvider"); var _types = require("../ArrowKeyNavigationProvider/types"); var _excluded = ["children"]; var _templateObject, _templateObject2, _templateObject3; /* eslint-disable @repo/internal/react/no-class-components */ /** * @jsxRuntime classic * @jsx jsx */ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled, @typescript-eslint/consistent-type-imports -- Ignored via go/DSP-18766; jsx required at runtime for @jsxRuntime classic 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); } 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; } 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; } function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } var wrapper = (0, _react2.css)({ /* tooltip in ToolbarButton is display:block */ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766 '& > div > div': { display: 'flex' } }); var focusedMenuItemStyle = (0, _react2.css)({ boxShadow: "inset 0px 0px 0px 2px ".concat("var(--ds-border-focused, #4688EC)"), outline: 'none' }); var buttonStyles = function buttonStyles(isActive, submenuActive) { if (isActive) { if ((0, _experiments.editorExperiment)('platform_editor_controls', 'variant1')) { /** * Hack for item to imitate old dropdown-menu selected styles */ // eslint-disable-next-line @atlaskit/design-system/no-css-tagged-template-expression -- needs manual remediation return (0, _react2.css)(_templateObject || (_templateObject = (0, _taggedTemplateLiteral2.default)(["\n\t\t\t\tposition: relative;\n\t\t\t\t&::before {\n\t\t\t\t\tdisplay: block;\n\t\t\t\t\theight: 100%;\n\t\t\t\t\twidth: 2px;\n\t\t\t\t\tposition: absolute;\n\t\t\t\t\tleft: 0;\n\t\t\t\t\ttop: 0;\n\t\t\t\t\tbackground: ", ";\n\t\t\t\t\tcontent: '';\n\t\t\t\t}\n\t\t\t\t> span,\n\t\t\t\t> span:hover,\n\t\t\t\t> span:active {\n\t\t\t\t\tbackground: ", ";\n\t\t\t\t\tcolor: ", ";\n\t\t\t\t}\n\t\t\t\t:focus > span[aria-disabled='false'] {\n\t\t\t\t\t", ";\n\t\t\t\t}\n\t\t\t\t:focus-visible,\n\t\t\t\t:focus-visible > span[aria-disabled='false'] {\n\t\t\t\t\toutline: none;\n\t\t\t\t}\n\t\t\t"])), "var(--ds-border-selected, #1868DB)", "var(--ds-background-selected, #E9F2FE)", "var(--ds-text-selected, #1868DB)", focusedMenuItemStyle); } /** * Hack for item to imitate old dropdown-menu selected styles */ // eslint-disable-next-line @atlaskit/design-system/no-css-tagged-template-expression -- needs manual remediation return (0, _react2.css)(_templateObject2 || (_templateObject2 = (0, _taggedTemplateLiteral2.default)(["\n\t\t\t> span,\n\t\t\t> span:hover,\n\t\t\t> span:active {\n\t\t\t\tbackground: ", ";\n\t\t\t\tcolor: ", ";\n\t\t\t}\n\t\t\t:focus > span[aria-disabled='false'] {\n\t\t\t\t", ";\n\t\t\t}\n\t\t\t:focus-visible,\n\t\t\t:focus-visible > span[aria-disabled='false'] {\n\t\t\t\toutline: none;\n\t\t\t}\n\t\t"])), "var(--ds-background-selected, #E9F2FE)", "var(--ds-text-selected, #1868DB)", focusedMenuItemStyle); } else { // eslint-disable-next-line @atlaskit/design-system/no-css-tagged-template-expression -- needs manual remediation return (0, _react2.css)(_templateObject3 || (_templateObject3 = (0, _taggedTemplateLiteral2.default)(["\n\t\t\t> span:hover[aria-disabled='false'] {\n\t\t\t\tcolor: ", ";\n\t\t\t\tbackground-color: ", ";\n\t\t\t}\n\t\t\t", "\n\t\t\t> span[aria-disabled='true'] {\n\t\t\t\tcolor: ", ";\n\t\t\t}\n\t\t\t:focus > span[aria-disabled='false'] {\n\t\t\t\t", ";\n\t\t\t}\n\t\t\t:focus-visible,\n\t\t\t:focus-visible > span[aria-disabled='false'] {\n\t\t\t\toutline: none;\n\t\t\t}\n\t\t"])), "var(--ds-text, #292A2E)", "var(--ds-background-neutral-subtle-hovered, #0515240F)", !submenuActive && "\n\t\t\t\t\t> span:active[aria-disabled='false'] {\n\t\t\t\t\t\tbackground-color: ".concat("var(--ds-background-neutral-subtle-pressed, #0B120E24)", ";\n\t\t\t\t\t}"), "var(--ds-text-disabled, #080F214A)", focusedMenuItemStyle); // The default focus-visible style is removed to ensure consistency across browsers } }; var DropListWithOutsideClickTargetRef = function DropListWithOutsideClickTargetRef(props) { var setOutsideClickTargetRef = _react.default.useContext(_uiReact.OutsideClickTargetRefContext); // eslint-disable-next-line react/jsx-props-no-spreading -- Spreading props to pass through dynamic component props return (0, _react2.jsx)(_DropList.default, (0, _extends2.default)({ onDroplistRef: setOutsideClickTargetRef }, props)); }; var DropListWithOutsideListeners = (0, _uiReact.withReactEditorViewOuterListeners)(DropListWithOutsideClickTargetRef); /** * Wrapper around @atlaskit/droplist which uses Popup and Portal to render * dropdown-menu outside of "overflow: hidden" containers when needed. * * Also it controls popper's placement. */ // Ignored via go/ees005 // eslint-disable-next-line @repo/internal/react/no-class-components var DropdownMenuWrapper = exports.default = /*#__PURE__*/function (_PureComponent) { function DropdownMenuWrapper() { var _this; (0, _classCallCheck2.default)(this, DropdownMenuWrapper); for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this = _callSuper(this, DropdownMenuWrapper, [].concat(args)); (0, _defineProperty2.default)(_this, "state", { popupPlacement: ['bottom', 'left'], selectionIndex: -1 }); (0, _defineProperty2.default)(_this, "popupRef", /*#__PURE__*/_react.default.createRef()); (0, _defineProperty2.default)(_this, "handleRef", function (target) { _this.setState({ target: target || undefined }); }); (0, _defineProperty2.default)(_this, "updatePopupPlacement", function (placement) { var previousPlacement = _this.state.popupPlacement; if (placement[0] !== previousPlacement[0] || placement[1] !== previousPlacement[1]) { _this.setState({ popupPlacement: placement }); } }); (0, _defineProperty2.default)(_this, "handleCloseAndFocus", function (event) { var _this$state$target; (_this$state$target = _this.state.target) === null || _this$state$target === void 0 || (_this$state$target = _this$state$target.querySelector('button')) === null || _this$state$target === void 0 || _this$state$target.focus(); _this.handleClose(event); }); (0, _defineProperty2.default)(_this, "handleClose", function (event) { var onOpenChange = _this.props.onOpenChange; if (onOpenChange) { onOpenChange({ isOpen: false, event: event }); } }); (0, _defineProperty2.default)(_this, "handleEnterKeydown", function (e) { if (!_this.props.allowEnterDefaultBehavior) { e.preventDefault(); } e.stopPropagation(); }); return _this; } (0, _inherits2.default)(DropdownMenuWrapper, _PureComponent); return (0, _createClass2.default)(DropdownMenuWrapper, [{ key: "renderDropdownMenu", value: function renderDropdownMenu() { var _this2 = this; var _this$state = this.state, target = _this$state.target, popupPlacement = _this$state.popupPlacement; var _this$props = this.props, items = _this$props.items, mountTo = _this$props.mountTo, boundariesElement = _this$props.boundariesElement, scrollableElement = _this$props.scrollableElement, offset = _this$props.offset, fitHeight = _this$props.fitHeight, fitWidth = _this$props.fitWidth, isOpen = _this$props.isOpen, zIndex = _this$props.zIndex, shouldUseDefaultRole = _this$props.shouldUseDefaultRole, onItemActivated = _this$props.onItemActivated, arrowKeyNavigationProviderOptions = _this$props.arrowKeyNavigationProviderOptions, section = _this$props.section, handleEscapeKeydown = _this$props.handleEscapeKeydown; // Note that this onSelection function can't be refactored to useMemo for // performance gains as it is being used as a dependency in a useEffect in // MenuArrowKeyNavigationProvider in order to check for re-renders to adjust // focus for accessibility. If this needs to be refactored in future refer // back to ED-16740 for context. var navigationProviderProps = arrowKeyNavigationProviderOptions.type === _types.ArrowKeyNavigationType.COLOR ? arrowKeyNavigationProviderOptions : _objectSpread(_objectSpread({}, arrowKeyNavigationProviderOptions), {}, { onSelection: function onSelection(index) { var result = []; if (typeof onItemActivated === 'function') { result = items.reduce(function (result, group) { return result.concat(group.items); }, result); onItemActivated({ item: result[index], shouldCloseMenu: false }); } } }); return (0, _react2.jsx)(_Popup.default, { target: isOpen ? target : undefined, mountTo: mountTo, boundariesElement: boundariesElement, scrollableElement: scrollableElement, onPlacementChanged: this.updatePopupPlacement, fitHeight: fitHeight, fitWidth: fitWidth, zIndex: zIndex || _editorSharedStyles.akEditorFloatingPanelZIndex, offset: offset }, (0, _react2.jsx)(_ArrowKeyNavigationProvider.ArrowKeyNavigationProvider // eslint-disable-next-line react/jsx-props-no-spreading -- Spreading navigationProviderProps to pass through dynamic component props , (0, _extends2.default)({}, navigationProviderProps, { handleClose: this.handleCloseAndFocus, closeOnTab: true }), (0, _react2.jsx)(DropListWithOutsideListeners, { isOpen: true, position: popupPlacement.join(' '), shouldFitContainer: true, handleClickOutside: this.handleClose, handleEscapeKeydown: handleEscapeKeydown || this.handleCloseAndFocus, handleEnterKeydown: this.handleEnterKeydown, targetRef: this.state.target }, (0, _react2.jsx)("div", { style: { height: 0, minWidth: fitWidth || 0 } }), (0, _react2.jsx)("div", { ref: this.popupRef }, (0, _react2.jsx)(_menu.MenuGroup, { role: shouldUseDefaultRole ? 'group' : 'menu' }, items.map(function (group, index) { return (0, _react2.jsx)(_menu.Section, { hasSeparator: (section === null || section === void 0 ? void 0 : section.hasSeparator) && index > 0, title: section === null || section === void 0 ? void 0 : section.title // Ignored via go/ees005 // eslint-disable-next-line react/no-array-index-key , key: index }, group.items.map(function (item) { var _item$key; return (0, _react2.jsx)(DropdownMenuItem, { key: (_item$key = item.key) !== null && _item$key !== void 0 ? _item$key : String(item.content), item: item, onItemActivated: _this2.props.onItemActivated, shouldUseDefaultRole: _this2.props.shouldUseDefaultRole, onMouseEnter: _this2.props.onMouseEnter, onMouseLeave: _this2.props.onMouseLeave }); })); })))))); } }, { key: "render", value: function render() { var _this$props2 = this.props, children = _this$props2.children, isOpen = _this$props2.isOpen; return (0, _react2.jsx)("div", { css: wrapper }, (0, _react2.jsx)("div", { ref: this.handleRef }, children), isOpen ? this.renderDropdownMenu() : null); } }, { key: "componentDidUpdate", value: function componentDidUpdate(previousProps) { var _this$props3 = this.props, mountTo = _this$props3.mountTo, isOpen = _this$props3.isOpen; var isOpenToggled = isOpen !== previousProps.isOpen; if (isOpen && isOpenToggled) { if (typeof this.props.shouldFocusFirstItem === 'function' && this.props.shouldFocusFirstItem()) { var _this$state$target2; var keyboardEvent = new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true }); if (mountTo) { mountTo.dispatchEvent(keyboardEvent); return; } (_this$state$target2 = this.state.target) === null || _this$state$target2 === void 0 || _this$state$target2.dispatchEvent(keyboardEvent); } } } }]); }(_react.PureComponent); var DropdownMenuItemCustomComponent = /*#__PURE__*/_react.default.forwardRef(function (props, ref) { var children = props.children, rest = (0, _objectWithoutProperties2.default)(props, _excluded); return (0, _react2.jsx)("span", (0, _extends2.default)({ ref: ref // eslint-disable-next-line react/jsx-props-no-spreading -- Spreading rest to pass through dynamic component props }, rest, { style: { // This forces the item container back to be `position: static`, the default value. // This ensures the custom nested menu for table color picker still works as now // menu items from @atlaskit/menu all have `position: relative` set for the selected borders. // The current implementation unfortunately is very brittle. Design System Team will // be prioritizing official support for accessible nested menus that we want you to move // to in the future. // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766 position: 'static' } }), children); }); function DropdownMenuItem(_ref) { var _item$key2; var item = _ref.item, onItemActivated = _ref.onItemActivated, shouldUseDefaultRole = _ref.shouldUseDefaultRole, onMouseEnter = _ref.onMouseEnter, onMouseLeave = _ref.onMouseLeave; var _React$useState = _react.default.useState(false), _React$useState2 = (0, _slicedToArray2.default)(_React$useState, 2), submenuActive = _React$useState2[0], setSubmenuActive = _React$useState2[1]; var memoizedOnClick = (0, _react.useCallback)(function () { return onItemActivated && onItemActivated({ item: item }); }, [onItemActivated, item]); var onClick = (0, _expValEquals.expValEquals)('platform_editor_perf_lint_cleanup', 'isEnabled', true) ? memoizedOnClick : function () { return onItemActivated && onItemActivated({ item: item }); }; var memoizedOnMouseDown = (0, _react.useCallback)(function (e) { e.preventDefault(); }, []); var onMouseDown = (0, _expValEquals.expValEquals)('platform_editor_perf_lint_cleanup', 'isEnabled', true) ? memoizedOnMouseDown : function (e) { e.preventDefault(); }; var memoizedOnMouseEnter = (0, _react.useCallback)(function () { return onMouseEnter && onMouseEnter({ item: item }); }, [onMouseEnter, item]); var onMouseEnterHandler = (0, _expValEquals.expValEquals)('platform_editor_perf_lint_cleanup', 'isEnabled', true) ? memoizedOnMouseEnter : function () { return onMouseEnter && onMouseEnter({ item: item }); }; var memoizedOnMouseLeave = (0, _react.useCallback)(function () { return onMouseLeave && onMouseLeave({ item: item }); }, [onMouseLeave, item]); var onMouseLeaveHandler = (0, _expValEquals.expValEquals)('platform_editor_perf_lint_cleanup', 'isEnabled', true) ? memoizedOnMouseLeave : function () { return onMouseLeave && onMouseLeave({ item: item }); }; // onClick and value.name are the action indicators in the handlers // If neither are present, don't wrap in an Item. if (!item.onClick && !(item.value && item.value.name)) { return (0, _react2.jsx)("span", { key: String(item.content) }, item.content); } var _handleSubmenuActive = function _handleSubmenuActive(event) { setSubmenuActive(Boolean(event.target instanceof HTMLElement && event.target.closest(".".concat(_styles.DropdownMenuSharedCssClassName.SUBMENU)))); }; var ariaLabel = item['aria-label'] === '' ? undefined : item['aria-label'] || String(item.content); var testId = item['data-testid'] || "dropdown-item__".concat(item.content); // From time to time we don't want to have any tabIndex on item wrapper // especially when we pass any interactive element as a item.content var tabIndex = item.wrapperTabIndex === null ? undefined : item.wrapperTabIndex || -1; var dropListItem = (0, _react2.jsx)("div", { css: function css() { return buttonStyles(item.isActive, submenuActive); }, role: (0, _expValEquals.expValEquals)('platform_editor_august_a11y', 'isEnabled', true) ? shouldUseDefaultRole ? undefined : 'menuitem' : undefined, tabIndex: tabIndex, "aria-disabled": item.isDisabled ? 'true' : 'false', "aria-expanded": (0, _expValEquals.expValEquals)('platform_editor_august_a11y', 'isEnabled', true) ? item['aria-expanded'] : undefined, onMouseDown: _handleSubmenuActive }, (0, _react2.jsx)(_menu.CustomItem, { item: item, key: (_item$key2 = item.key) !== null && _item$key2 !== void 0 ? _item$key2 : String(item.content), testId: testId, role: shouldUseDefaultRole ? 'button' : (0, _expValEquals.expValEquals)('platform_editor_august_a11y', 'isEnabled', true) ? undefined : 'menuitem', iconBefore: item.elemBefore, iconAfter: item.elemAfter, isDisabled: item.isDisabled, onClick: onClick, "aria-label": ariaLabel, "aria-pressed": shouldUseDefaultRole ? item.isActive : undefined, "aria-keyshortcuts": item['aria-keyshortcuts'], onMouseDown: onMouseDown, component: DropdownMenuItemCustomComponent, onMouseEnter: onMouseEnterHandler, onMouseLeave: onMouseLeaveHandler, "aria-expanded": (0, _expValEquals.expValEquals)('platform_editor_august_a11y', 'isEnabled', true) ? undefined : item['aria-expanded'] }, item.content)); if (item.tooltipDescription) { var _item$key3; return (0, _react2.jsx)(_tooltip.default, { key: (_item$key3 = item.key) !== null && _item$key3 !== void 0 ? _item$key3 : String(item.content), content: item.tooltipDescription, position: item.tooltipPosition }, dropListItem); } return dropListItem; } var DropdownMenuWithKeyboardNavigation = exports.DropdownMenuWithKeyboardNavigation = /*#__PURE__*/_react.default.memo( // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any function (_ref2) { var props = (0, _extends2.default)({}, ((0, _objectDestructuringEmpty2.default)(_ref2), _ref2)); var keyDownHandlerContext = (0, _react.useContext)(_ToolbarArrowKeyNavigationProvider.KeyDownHandlerContext); // This context is to handle the tab, Arrow Right/Left key events for dropdown. // Default context has the void callbacks for above key events var memoizedArrowKeyNavOptions = (0, _react.useMemo)(function () { return _objectSpread(_objectSpread({}, props.arrowKeyNavigationProviderOptions), {}, { keyDownHandlerContext: keyDownHandlerContext }); }, [props.arrowKeyNavigationProviderOptions, keyDownHandlerContext]); var arrowKeyNavOptions = (0, _expValEquals.expValEquals)('platform_editor_perf_lint_cleanup', 'isEnabled', true) ? memoizedArrowKeyNavOptions : _objectSpread(_objectSpread({}, props.arrowKeyNavigationProviderOptions), {}, { keyDownHandlerContext: keyDownHandlerContext }); return (0, _react2.jsx)(DropdownMenuWrapper, (0, _extends2.default)({ arrowKeyNavigationProviderOptions: arrowKeyNavOptions // eslint-disable-next-line react/jsx-props-no-spreading -- Spreading props to pass through dynamic component props }, props)); });