@atlaskit/editor-common
Version:
A package that contains common classes and components for editor and renderer
345 lines (340 loc) • 19.6 kB
JavaScript
;
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 _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
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 _editorSharedStyles = require("@atlaskit/editor-shared-styles");
var _menu = require("@atlaskit/menu");
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
var _colors = require("@atlaskit/theme/colors");
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, _templateObject4;
/** @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 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 _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; })(); }
var wrapper = (0, _react2.css)(_templateObject || (_templateObject = (0, _taggedTemplateLiteral2.default)(["\n /* tooltip in ToolbarButton is display:block */\n & > div > div {\n display: flex;\n }\n"])));
var focusedMenuItemStyle = (0, _react2.css)(_templateObject2 || (_templateObject2 = (0, _taggedTemplateLiteral2.default)(["\n box-shadow: inset 0px 0px 0px 2px ", ";\n outline: none;\n"])), "var(--ds-border-focused, ".concat(_colors.B100, ")"));
var buttonStyles = function buttonStyles(isActive, submenuActive) {
if (isActive) {
/**
* Hack for item to imitate old dropdown-menu selected styles
*/
return (0, _react2.css)(_templateObject3 || (_templateObject3 = (0, _taggedTemplateLiteral2.default)(["\n > span,\n > span:hover,\n > span:active {\n background: ", ";\n color: ", ";\n }\n :focus > span[aria-disabled='false'] {\n ", ";\n }\n :focus-visible,\n :focus-visible > span[aria-disabled='false'] {\n outline: none;\n }\n "])), "var(--ds-background-selected, #6c798f)", "var(--ds-text, #fff)", focusedMenuItemStyle);
} else {
return (0, _react2.css)(_templateObject4 || (_templateObject4 = (0, _taggedTemplateLiteral2.default)(["\n > span:hover[aria-disabled='false'] {\n color: ", ";\n background-color: ", ";\n }\n ", "\n > span[aria-disabled='true'] {\n color: ", ";\n }\n :focus > span[aria-disabled='false'] {\n ", ";\n }\n :focus-visible,\n :focus-visible > span[aria-disabled='false'] {\n outline: none;\n }\n "])), "var(--ds-text, ".concat(_colors.N900, ")"), "var(--ds-background-neutral-subtle-hovered, rgb(244, 245, 247))", !submenuActive && "\n > span:active[aria-disabled='false'] {\n background-color: ".concat("var(--ds-background-neutral-subtle-pressed, rgb(179, 212, 255))", ";\n }"), "var(--ds-text-disabled, ".concat(_colors.N70, ")"), focusedMenuItemStyle); // The default focus-visible style is removed to ensure consistency across browsers
}
};
var DropListWithOutsideListeners = (0, _uiReact.withReactEditorViewOuterListeners)(_DropList.default);
/**
* 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.
*/
var DropdownMenuWrapper = exports.default = /*#__PURE__*/function (_PureComponent) {
(0, _inherits2.default)(DropdownMenuWrapper, _PureComponent);
var _super = _createSuper(DropdownMenuWrapper);
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 = _super.call.apply(_super, [this].concat(args));
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "state", {
popupPlacement: ['bottom', 'left'],
selectionIndex: -1
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "popupRef", /*#__PURE__*/_react.default.createRef());
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "handleRef", function (target) {
_this.setState({
target: target || undefined
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.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)((0, _assertThisInitialized2.default)(_this), "handleCloseAndFocus", function () {
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();
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "handleClose", function () {
if (_this.props.onOpenChange) {
_this.props.onOpenChange({
isOpen: false
});
}
});
return _this;
}
(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;
// 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, (0, _extends2.default)({}, navigationProviderProps, {
handleClose: this.handleCloseAndFocus,
closeOnTab: true
}), (0, _react2.jsx)(DropListWithOutsideListeners, {
isOpen: true,
appearance: "tall",
position: popupPlacement.join(' '),
shouldFlip: false,
shouldFitContainer: true,
isTriggerNotTabbable: true,
handleClickOutside: this.handleClose,
handleEscapeKeydown: this.handleCloseAndFocus,
handleEnterKeydown: function handleEnterKeydown(e) {
e.preventDefault();
e.stopPropagation();
},
targetRef: this.state.target
}, (0, _react2.jsx)("div", {
style: {
height: 0,
minWidth: fitWidth || 0
}
}), (0, _react2.jsx)("div", {
ref: this.popupRef
}, (0, _platformFeatureFlags.getBooleanFF)('platform.editor.menu.group-items') && (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,
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
});
}));
})), !(0, _platformFeatureFlags.getBooleanFF)('platform.editor.menu.group-items') && items.map(function (group, index) {
return (0, _react2.jsx)(_menu.MenuGroup, {
key: index,
role: shouldUseDefaultRole ? 'group' : 'menu'
}, group.items.map(function (item) {
var _item$key2;
return (0, _react2.jsx)(DropdownMenuItem, {
key: (_item$key2 = item.key) !== null && _item$key2 !== void 0 ? _item$key2 : 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 isOpenToggled = this.props.isOpen !== previousProps.isOpen;
if (this.props.isOpen && isOpenToggled) {
if (typeof this.props.shouldFocusFirstItem === 'function' && this.props.shouldFocusFirstItem()) {
var _this$state$target2;
var keyboardEvent = new KeyboardEvent('keydown', {
key: 'ArrowDown',
bubbles: true
});
(_this$state$target2 = this.state.target) === null || _this$state$target2 === void 0 || _this$state$target2.dispatchEvent(keyboardEvent);
}
}
}
}]);
return DropdownMenuWrapper;
}(_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
}, 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.
position: 'static'
}
}), children);
});
function DropdownMenuItem(_ref) {
var _item$key3;
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];
// 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(!!event.target.closest(".".concat(_styles.DropdownMenuSharedCssClassName.SUBMENU)));
};
var dropListItem = (0, _react2.jsx)("div", {
css: function css() {
return buttonStyles(item.isActive, submenuActive);
},
tabIndex: -1,
"aria-disabled": item.isDisabled ? 'true' : 'false',
onMouseDown: _handleSubmenuActive
}, (0, _react2.jsx)(_menu.CustomItem, {
item: item,
key: (_item$key3 = item.key) !== null && _item$key3 !== void 0 ? _item$key3 : String(item.content),
testId: "dropdown-item__".concat(String(item.content)),
role: shouldUseDefaultRole ? 'button' : 'menuitem',
iconBefore: item.elemBefore,
iconAfter: item.elemAfter,
isDisabled: item.isDisabled,
onClick: function onClick() {
return onItemActivated && onItemActivated({
item: item
});
},
"aria-label": item['aria-label'] || String(item.content),
"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
});
}
}, item.content));
if (item.tooltipDescription) {
var _item$key4;
return (0, _react2.jsx)(_tooltip.default, {
key: (_item$key4 = item.key) !== null && _item$key4 !== void 0 ? _item$key4 : String(item.content),
content: item.tooltipDescription,
position: item.tooltipPosition
}, dropListItem);
}
return dropListItem;
}
var DropdownMenuWithKeyboardNavigation = exports.DropdownMenuWithKeyboardNavigation = /*#__PURE__*/_react.default.memo(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
return (0, _react2.jsx)(DropdownMenuWrapper, (0, _extends2.default)({
arrowKeyNavigationProviderOptions: _objectSpread(_objectSpread({}, props.arrowKeyNavigationProviderOptions), {}, {
keyDownHandlerContext: keyDownHandlerContext
})
}, props));
});