@atlaskit/editor-plugin-floating-toolbar
Version:
Floating toolbar plugin for @atlaskit/editor-core
277 lines (273 loc) • 13.6 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
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 _react = _interopRequireWildcard(require("react"));
var _react2 = require("@emotion/react");
var _ui = require("@atlaskit/editor-common/ui");
var _uiMenu = require("@atlaskit/editor-common/ui-menu");
var _chevronDown = _interopRequireDefault(require("@atlaskit/icon/core/chevron-down"));
var _Divider = require("./Divider");
var _DropdownMenu = _interopRequireWildcard(require("./DropdownMenu"));
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; })(); } /**
* @jsxRuntime classic
* @jsx jsx
* @jsxFrag
*/ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled, @typescript-eslint/consistent-type-imports
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
var dropdownExpandContainer = (0, _react2.css)({
margin: "0px ".concat("var(--ds-space-negative-050, -4px)")
});
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
var iconGroup = (0, _react2.css)({
display: 'flex'
});
var CompositeIcon = function CompositeIcon(_ref) {
var icon = _ref.icon;
return (0, _react2.jsx)("div", {
css: iconGroup
}, icon, (0, _react2.jsx)("span", {
css: dropdownExpandContainer
}, (0, _react2.jsx)(_chevronDown.default, {
color: "currentColor",
spacing: "spacious",
label: "Expand dropdown menu",
size: "small"
})));
};
// eslint-disable-next-line @repo/internal/react/no-class-components
var Dropdown = exports.default = /*#__PURE__*/function (_Component) {
function Dropdown() {
var _this;
(0, _classCallCheck2.default)(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));
(0, _defineProperty2.default)(_this, "state", {
isOpen: false,
isOpenedByKeyboard: false
});
(0, _defineProperty2.default)(_this, "triggerRef", /*#__PURE__*/_react.default.createRef());
(0, _defineProperty2.default)(_this, "makeArrayOptionsFromCallback", function (makeOptions) {
var options = makeOptions();
return options;
});
(0, _defineProperty2.default)(_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 (0, _react2.jsx)(_DropdownMenu.default, {
hide: _this.hide,
dispatchCommand: dispatchCommand,
items: options,
showSelected: showSelected,
editorView: editorView,
areAnyNewToolbarFlagsEnabled: areAnyNewToolbarFlagsEnabled
});
});
(0, _defineProperty2.default)(_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);
});
});
(0, _defineProperty2.default)(_this, "toggleOpenByKeyboard", function (event) {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
_this.setState({
isOpen: !_this.state.isOpen,
isOpenedByKeyboard: true
});
}
});
(0, _defineProperty2.default)(_this, "hide", function () {
_this.setState(_objectSpread(_objectSpread({}, _this.state), {}, {
isOpen: false
}));
});
(0, _defineProperty2.default)(_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();
});
(0, _defineProperty2.default)(_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;
}
(0, _inherits2.default)(Dropdown, _Component);
return (0, _createClass2.default)(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 : (0, _react2.jsx)(CompositeIcon, {
icon: icon
});
trigger = (0, _react2.jsx)(_ui.FloatingToolbarButton, {
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 = (0, _react2.jsx)(_ui.FloatingToolbarButton, {
testId: buttonTestId,
iconAfter: (0, _react2.jsx)("span", {
css: dropdownExpandContainer
}, (0, _react2.jsx)(_chevronDown.default, {
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 || _DropdownMenu.menuItemDimensions.width : options.width;
var fitHeight = Array.isArray(options) ? options.length * _DropdownMenu.menuItemDimensions.height + _DropdownMenu.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
*/
(0, _react2.jsx)(_uiMenu.DropdownContainer, {
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: _uiMenu.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 && (0, _react2.jsx)(_react.default.Fragment, null, (0, _react2.jsx)(_Divider.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);
}
}
}
}
}]);
}(_react.Component);