UNPKG

@atlaskit/editor-common

Version:

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

373 lines (369 loc) • 18 kB
import _objectDestructuringEmpty from "@babel/runtime/helpers/objectDestructuringEmpty"; import _slicedToArray from "@babel/runtime/helpers/slicedToArray"; import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; import _createClass from "@babel/runtime/helpers/createClass"; import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn"; import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf"; import _inherits from "@babel/runtime/helpers/inherits"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; import _extends from "@babel/runtime/helpers/extends"; import _taggedTemplateLiteral from "@babel/runtime/helpers/taggedTemplateLiteral"; var _excluded = ["children"]; var _templateObject, _templateObject2; 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) { _defineProperty(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 = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(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; })(); } /* eslint-disable @repo/internal/react/no-class-components */ /** * @jsxRuntime classic * @jsx jsx */ import React, { PureComponent, useContext } from 'react'; // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766 import { css, jsx } from '@emotion/react'; import { akEditorFloatingPanelZIndex } from '@atlaskit/editor-shared-styles'; import { CustomItem, MenuGroup, Section } from '@atlaskit/menu'; import Tooltip from '@atlaskit/tooltip'; import { DropdownMenuSharedCssClassName } from '../../styles'; import { KeyDownHandlerContext } from '../../ui-menu/ToolbarArrowKeyNavigationProvider'; import { OutsideClickTargetRefContext, withReactEditorViewOuterListeners } from '../../ui-react'; import DropList from '../../ui/DropList'; import Popup from '../../ui/Popup'; import { ArrowKeyNavigationProvider } from '../ArrowKeyNavigationProvider'; import { ArrowKeyNavigationType } from '../ArrowKeyNavigationProvider/types'; var wrapper = 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 = css({ boxShadow: "inset 0px 0px 0px 2px ".concat("var(--ds-border-focused, #388BFF)"), outline: 'none' }); var buttonStyles = function buttonStyles(isActive, submenuActive) { if (isActive) { /** * 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 css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\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, #6c798f)", "var(--ds-text, #fff)", focusedMenuItemStyle); } else { // eslint-disable-next-line @atlaskit/design-system/no-css-tagged-template-expression -- needs manual remediation return css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\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, #172B4D)", "var(--ds-background-neutral-subtle-hovered, rgb(244, 245, 247))", !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, rgb(179, 212, 255))", ";\n\t\t\t\t\t}"), "var(--ds-text-disabled, #091E424F)", focusedMenuItemStyle); // The default focus-visible style is removed to ensure consistency across browsers } }; var DropListWithOutsideClickTargetRef = function DropListWithOutsideClickTargetRef(props) { var setOutsideClickTargetRef = React.useContext(OutsideClickTargetRefContext); // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading return jsx(DropList, _extends({ onDroplistRef: setOutsideClickTargetRef }, props)); }; var DropListWithOutsideListeners = 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 = /*#__PURE__*/function (_PureComponent) { function DropdownMenuWrapper() { var _this; _classCallCheck(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)); _defineProperty(_this, "state", { popupPlacement: ['bottom', 'left'], selectionIndex: -1 }); _defineProperty(_this, "popupRef", /*#__PURE__*/React.createRef()); _defineProperty(_this, "handleRef", function (target) { _this.setState({ target: target || undefined }); }); _defineProperty(_this, "updatePopupPlacement", function (placement) { var previousPlacement = _this.state.popupPlacement; if (placement[0] !== previousPlacement[0] || placement[1] !== previousPlacement[1]) { _this.setState({ popupPlacement: placement }); } }); _defineProperty(_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); }); _defineProperty(_this, "handleClose", function (event) { var onOpenChange = _this.props.onOpenChange; if (onOpenChange) { onOpenChange({ isOpen: false, event: event }); } }); return _this; } _inherits(DropdownMenuWrapper, _PureComponent); return _createClass(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, allowEnterDefaultBehavior = _this$props.allowEnterDefaultBehavior, 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 === 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 jsx(Popup, { target: isOpen ? target : undefined, mountTo: mountTo, boundariesElement: boundariesElement, scrollableElement: scrollableElement, onPlacementChanged: this.updatePopupPlacement, fitHeight: fitHeight, fitWidth: fitWidth, zIndex: zIndex || akEditorFloatingPanelZIndex, offset: offset }, jsx(ArrowKeyNavigationProvider // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading , _extends({}, navigationProviderProps, { handleClose: this.handleCloseAndFocus, closeOnTab: true }), jsx(DropListWithOutsideListeners, { isOpen: true, position: popupPlacement.join(' '), shouldFitContainer: true, handleClickOutside: this.handleClose, handleEscapeKeydown: handleEscapeKeydown || this.handleCloseAndFocus, handleEnterKeydown: function handleEnterKeydown(e) { if (!allowEnterDefaultBehavior) { e.preventDefault(); } e.stopPropagation(); }, targetRef: this.state.target }, jsx("div", { style: { height: 0, minWidth: fitWidth || 0 } }), jsx("div", { ref: this.popupRef }, jsx(MenuGroup, { role: shouldUseDefaultRole ? 'group' : 'menu' }, items.map(function (group, index) { return jsx(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 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 jsx("div", { css: wrapper }, 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); } } } }]); }(PureComponent); export { DropdownMenuWrapper as default }; var DropdownMenuItemCustomComponent = /*#__PURE__*/React.forwardRef(function (props, ref) { var children = props.children, rest = _objectWithoutProperties(props, _excluded); return jsx("span", _extends({ ref: ref // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading }, 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); }); export 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.useState(false), _React$useState2 = _slicedToArray(_React$useState, 2), submenuActive = _React$useState2[0], setSubmenuActive = _React$useState2[1]; // 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 jsx("span", { key: String(item.content) }, item.content); } var _handleSubmenuActive = function _handleSubmenuActive(event) { setSubmenuActive(Boolean(event.target instanceof HTMLElement && event.target.closest(".".concat(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 = // eslint-disable-next-line jsx-a11y/no-static-element-interactions jsx("div", { css: function css() { return buttonStyles(item.isActive, submenuActive); }, tabIndex: tabIndex, "aria-disabled": item.isDisabled ? 'true' : 'false', onMouseDown: _handleSubmenuActive }, jsx(CustomItem, { item: item, key: (_item$key2 = item.key) !== null && _item$key2 !== void 0 ? _item$key2 : String(item.content), testId: testId, role: shouldUseDefaultRole ? 'button' : 'menuitem', iconBefore: item.elemBefore, iconAfter: item.elemAfter, isDisabled: item.isDisabled, onClick: function onClick() { return onItemActivated && onItemActivated({ item: item }); }, "aria-label": ariaLabel, "aria-pressed": shouldUseDefaultRole ? item.isActive : undefined, "aria-keyshortcuts": item['aria-keyshortcuts'], onMouseDown: function onMouseDown(e) { e.preventDefault(); }, component: DropdownMenuItemCustomComponent, onMouseEnter: function onMouseEnter() { return _onMouseEnter && _onMouseEnter({ item: item }); }, onMouseLeave: function onMouseLeave() { return _onMouseLeave && _onMouseLeave({ item: item }); }, "aria-expanded": item['aria-expanded'] }, item.content)); if (item.tooltipDescription) { var _item$key3; return jsx(Tooltip, { key: (_item$key3 = item.key) !== null && _item$key3 !== void 0 ? _item$key3 : String(item.content), content: item.tooltipDescription, position: item.tooltipPosition }, dropListItem); } return dropListItem; } export var DropdownMenuWithKeyboardNavigation = /*#__PURE__*/React.memo( // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any function (_ref2) { var props = _extends({}, (_objectDestructuringEmpty(_ref2), _ref2)); var keyDownHandlerContext = useContext(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 return jsx(DropdownMenuWrapper, _extends({ arrowKeyNavigationProviderOptions: _objectSpread(_objectSpread({}, props.arrowKeyNavigationProviderOptions), {}, { keyDownHandlerContext: keyDownHandlerContext }) // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading }, props)); });