@atlaskit/editor-plugin-floating-toolbar
Version:
Floating toolbar plugin for @atlaskit/editor-core
274 lines (268 loc) • 12.1 kB
JavaScript
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";
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; })(); }
/**
* @jsxRuntime classic
* @jsx jsx
* @jsxFrag
*/
import React, { Component } from 'react';
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled, @typescript-eslint/consistent-type-imports
import { css, jsx } from '@emotion/react';
import { FloatingToolbarButton as Button } from '@atlaskit/editor-common/ui';
import { ArrowKeyNavigationType, DropdownContainer as UiDropdown } from '@atlaskit/editor-common/ui-menu';
import ChevronDownIcon from '@atlaskit/icon/core/chevron-down';
import { Divider } from './Divider';
import DropdownMenu, { itemSpacing, menuItemDimensions } from './DropdownMenu';
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
var dropdownExpandContainer = css({
margin: "0px ".concat("var(--ds-space-negative-050, -4px)")
});
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
var iconGroup = css({
display: 'flex'
});
var CompositeIcon = function CompositeIcon(_ref) {
var icon = _ref.icon;
return jsx("div", {
css: iconGroup
}, icon, jsx("span", {
css: dropdownExpandContainer
}, jsx(ChevronDownIcon, {
color: "currentColor",
spacing: "spacious",
label: "Expand dropdown menu",
size: "small"
})));
};
// eslint-disable-next-line @repo/internal/react/no-class-components
var Dropdown = /*#__PURE__*/function (_Component) {
function Dropdown() {
var _this;
_classCallCheck(this, Dropdown);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _callSuper(this, Dropdown, [].concat(args));
_defineProperty(_this, "state", {
isOpen: false,
isOpenedByKeyboard: false
});
_defineProperty(_this, "triggerRef", /*#__PURE__*/React.createRef());
_defineProperty(_this, "makeArrayOptionsFromCallback", function (makeOptions) {
var options = makeOptions();
return options;
});
_defineProperty(_this, "renderArrayOptions", function (options) {
var _this$props = _this.props,
showSelected = _this$props.showSelected,
dispatchCommand = _this$props.dispatchCommand,
editorView = _this$props.editorView,
areAnyNewToolbarFlagsEnabled = _this$props.areAnyNewToolbarFlagsEnabled;
return jsx(DropdownMenu, {
hide: _this.hide,
dispatchCommand: dispatchCommand,
items: options,
showSelected: showSelected,
editorView: editorView,
areAnyNewToolbarFlagsEnabled: areAnyNewToolbarFlagsEnabled
});
});
_defineProperty(_this, "toggleOpen", function () {
var onClick = _this.props.onClick;
if (onClick) {
onClick();
}
_this.setState({
isOpen: !_this.state.isOpen,
isOpenedByKeyboard: false
});
var onToggle = _this.props.onToggle;
if (!onToggle) {
return;
}
requestAnimationFrame(function () {
_this.props.dispatchCommand(onToggle);
});
});
_defineProperty(_this, "toggleOpenByKeyboard", function (event) {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
_this.setState({
isOpen: !_this.state.isOpen,
isOpenedByKeyboard: true
});
}
});
_defineProperty(_this, "hide", function () {
_this.setState(_objectSpread(_objectSpread({}, _this.state), {}, {
isOpen: false
}));
});
_defineProperty(_this, "hideOnEsc", function () {
var _document$querySelect;
// Focus the trigger button only on Escape
// Focus is done before hiding to ensure onBlur is called
// Ignored via go/ees005
// eslint-disable-next-line @atlaskit/editor/no-as-casting
(_document$querySelect = document.querySelector("[data-testid=".concat(_this.props.buttonTestId, "]"))) === null || _document$querySelect === void 0 || _document$querySelect.focus();
_this.hide();
});
_defineProperty(_this, "onOpenChanged", function (openChangedEvent) {
if (!openChangedEvent.isOpen && openChangedEvent.event instanceof KeyboardEvent) {
var _openChangedEvent$eve;
((_openChangedEvent$eve = openChangedEvent.event) === null || _openChangedEvent$eve === void 0 ? void 0 : _openChangedEvent$eve.key) === 'Escape' ? _this.hideOnEsc() : _this.hide();
}
});
return _this;
}
_inherits(Dropdown, _Component);
return _createClass(Dropdown, [{
key: "render",
value: function render() {
var isOpen = this.state.isOpen;
var _this$props2 = this.props,
title = _this$props2.title,
icon = _this$props2.icon,
iconBefore = _this$props2.iconBefore,
options = _this$props2.options,
dispatchCommand = _this$props2.dispatchCommand,
mountPoint = _this$props2.mountPoint,
boundariesElement = _this$props2.boundariesElement,
scrollableElement = _this$props2.scrollableElement,
hideExpandIcon = _this$props2.hideExpandIcon,
disabled = _this$props2.disabled,
tooltip = _this$props2.tooltip,
buttonTestId = _this$props2.buttonTestId,
dropdownWidth = _this$props2.dropdownWidth,
dropdownListId = _this$props2.dropdownListId,
alignDropdownWithToolbar = _this$props2.alignDropdownWithToolbar,
footer = _this$props2.footer,
onMount = _this$props2.onMount,
pulse = _this$props2.pulse,
shouldFitContainer = _this$props2.shouldFitContainer,
alignX = _this$props2.alignX,
areAnyNewToolbarFlagsEnabled = _this$props2.areAnyNewToolbarFlagsEnabled;
var trigger;
if (icon) {
var TriggerIcon = hideExpandIcon ? icon : jsx(CompositeIcon, {
icon: icon
});
trigger = jsx(Button, {
testId: buttonTestId,
title: title,
icon: TriggerIcon,
onClick: this.toggleOpen,
onKeyDown: this.toggleOpenByKeyboard,
selected: isOpen,
disabled: disabled,
tooltipContent: tooltip,
ariaHasPopup: areAnyNewToolbarFlagsEnabled ? true : undefined,
onMount: onMount,
pulse: pulse,
areAnyNewToolbarFlagsEnabled: areAnyNewToolbarFlagsEnabled
});
} else {
trigger = jsx(Button, {
testId: buttonTestId,
iconAfter: jsx("span", {
css: dropdownExpandContainer
}, jsx(ChevronDownIcon, {
color: "currentColor",
spacing: "spacious",
label: "Expand dropdown menu",
size: "small"
})),
icon: iconBefore,
onClick: this.toggleOpen,
onKeyDown: this.toggleOpenByKeyboard,
selected: isOpen,
disabled: disabled,
tooltipContent: tooltip,
ariaHasPopup: true,
areaControls: dropdownListId,
onMount: onMount,
pulse: pulse,
areAnyNewToolbarFlagsEnabled: areAnyNewToolbarFlagsEnabled
}, title);
}
/**
* We want to change direction of our dropdowns a bit early,
* not exactly when it hits the boundary.
*/
var fitTolerance = 10;
var fitWidth = Array.isArray(options) || typeof options === 'function' ? dropdownWidth || menuItemDimensions.width : options.width;
var fitHeight = Array.isArray(options) ? options.length * menuItemDimensions.height + itemSpacing * 2 : typeof options === 'function' ? this.makeArrayOptionsFromCallback(options).length : options.height;
return (
/**
* At the moment footer diver is rendered along with footer, if it's provided
* This is to provide some level of consistency
* Refer to the PR for more details:
* https://stash.atlassian.com/projects/ATLASSIAN/repos/atlassian-frontend-monorepo/pull-requests/137394/overview?commentId=8130003
*/
jsx(UiDropdown, {
alignX: alignX,
mountTo: mountPoint,
boundariesElement: boundariesElement,
scrollableElement: scrollableElement,
isOpen: isOpen,
handleClickOutside: this.hide,
handleEscapeKeydown: this.hideOnEsc,
onOpenChange: this.onOpenChanged,
fitWidth: fitWidth + fitTolerance,
fitHeight: fitHeight + fitTolerance,
trigger: trigger,
dropdownListId: dropdownListId,
alignDropdownWithParentElement: alignDropdownWithToolbar
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
,
arrowKeyNavigationProviderOptions: {
type: ArrowKeyNavigationType.MENU
},
shouldFitContainer: shouldFitContainer
}, Array.isArray(options) ? this.renderArrayOptions(options) : typeof options === 'function' ? this.renderArrayOptions(this.makeArrayOptionsFromCallback(options)) : options.render({
hide: this.hide,
dispatchCommand: dispatchCommand
}), footer && jsx(React.Fragment, null, jsx(Divider, null), footer))
);
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps, prevState) {
if (prevState.isOpen !== this.state.isOpen) {
if (this.props.setDisableParentScroll) {
this.props.setDisableParentScroll(this.state.isOpen);
}
// ECA11Y-235: no sense in sending keyboard event since the menu popup mounted to the custom element,
// we will ensure first element focused asap as 'MenuArrowKeyNavigationProvider' is mounted
if (this.props.mountPoint) {
return;
}
if (this.state.isOpen && this.state.isOpenedByKeyboard) {
var dropList = document.querySelector('[data-role="droplistContent"]');
if (dropList) {
// Add setTimeout so that if a dropdown item has tooltip,
// the tooltip won't be rendered until next render cycle
// when the droplist is correctly positioned.
// This makes tooltip appears at the correct position for the first dropdown item.
setTimeout(function () {
var keyboardEvent = new KeyboardEvent('keydown', {
bubbles: true,
key: 'ArrowDown'
});
dropList.dispatchEvent(keyboardEvent);
}, 0);
}
}
}
}
}]);
}(Component);
export { Dropdown as default };