@atlaskit/editor-plugin-floating-toolbar
Version:
Floating toolbar plugin for @atlaskit/editor-core
150 lines (149 loc) • 6.59 kB
JavaScript
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import React, { useEffect, useState } from 'react';
import { bind } from 'bind-event-listener';
import rafSchedule from 'raf-schd';
import { IconButton } from '@atlaskit/button/new';
import { messages } from '@atlaskit/editor-common/floating-toolbar';
import ChevronLeftLargeIcon from '@atlaskit/icon/core/chevron-left';
import ChevronRightLargeIcon from '@atlaskit/icon/core/chevron-right';
// eslint-disable-next-line @atlaskit/design-system/no-emotion-primitives -- to be migrated to @atlaskit/primitives/compiled – go/akcss
import { Box, xcss } from '@atlaskit/primitives';
var rightSideStyles = xcss({
borderLeft: "solid ".concat("var(--ds-border, #0B120E24)", " 1px"),
right: 'space.0',
borderTopRightRadius: 'radius.small',
borderBottomRightRadius: 'radius.small'
});
var leftSideStyles = xcss({
borderRight: "solid ".concat("var(--ds-border, #0B120E24)", " 1px"),
left: 'space.0',
borderTopLeftRadius: 'radius.small',
borderBottomLeftRadius: 'radius.small'
});
var buttonCommonStyles = xcss({
backgroundColor: 'elevation.surface.overlay',
zIndex: '1',
position: 'absolute'
});
export var ScrollButton = function ScrollButton(_ref) {
var intl = _ref.intl,
scrollContainerRef = _ref.scrollContainerRef,
node = _ref.node,
disabled = _ref.disabled,
side = _ref.side;
var _useState = useState(false),
_useState2 = _slicedToArray(_useState, 2),
needScroll = _useState2[0],
setNeedScroll = _useState2[1];
var _useState3 = useState(true),
_useState4 = _slicedToArray(_useState3, 2),
canScrollToSide = _useState4[0],
setCanScrollToSide = _useState4[1];
var setCanScrollDebounced = rafSchedule(function () {
// Refs are null before mounting and after unmount
if (!scrollContainerRef.current) {
return;
}
var _scrollContainerRef$c = scrollContainerRef.current,
scrollLeft = _scrollContainerRef$c.scrollLeft,
scrollWidth = _scrollContainerRef$c.scrollWidth,
offsetWidth = _scrollContainerRef$c.offsetWidth;
setCanScrollToSide(
// -1 to account for pixel rounding error
side === 'left' ? scrollLeft > 0 : scrollLeft < scrollWidth - offsetWidth - 1);
});
var onScroll = function onScroll() {
setCanScrollDebounced();
};
var onClick = function onClick() {
var _scrollContainerRef$c2, _scrollContainerRef$c3, _scrollContainerRef$c4;
var _ref2 = ((_scrollContainerRef$c2 = scrollContainerRef.current) === null || _scrollContainerRef$c2 === void 0 ? void 0 : _scrollContainerRef$c2.getBoundingClientRect()) || {},
_ref2$width = _ref2.width,
scrollContainerWidth = _ref2$width === void 0 ? 0 : _ref2$width;
var scrollLeft = ((_scrollContainerRef$c3 = scrollContainerRef.current) === null || _scrollContainerRef$c3 === void 0 ? void 0 : _scrollContainerRef$c3.scrollLeft) || 0;
var scrollTo = side === 'left' ? scrollLeft - scrollContainerWidth : scrollLeft + scrollContainerWidth;
(_scrollContainerRef$c4 = scrollContainerRef.current) === null || _scrollContainerRef$c4 === void 0 || _scrollContainerRef$c4.scrollTo({
top: 0,
left: scrollTo,
behavior: 'smooth'
});
};
var resizeObserver = new ResizeObserver(function (_t) {
var _scrollContainerRef$c5, _scrollContainerRef$c6;
var widthNeededToShowAllItems = ((_scrollContainerRef$c5 = scrollContainerRef.current) === null || _scrollContainerRef$c5 === void 0 ? void 0 : _scrollContainerRef$c5.scrollWidth) || 0;
var parentNode = (_scrollContainerRef$c6 = scrollContainerRef.current) === null || _scrollContainerRef$c6 === void 0 ? void 0 : _scrollContainerRef$c6.parentNode;
var availableSpace = -1;
if (parentNode instanceof HTMLElement) {
availableSpace = parentNode.offsetWidth;
}
if (availableSpace === -1) {
return;
}
if (availableSpace >= widthNeededToShowAllItems) {
setNeedScroll(false);
} else {
setNeedScroll(true);
onScroll();
}
});
useEffect(function () {
onScroll();
var scrollContainerRefCurrent = scrollContainerRef.current;
var unbind;
if (scrollContainerRefCurrent) {
// Adding/removing scroll button depending on scroll position
unbind = bind(scrollContainerRefCurrent, {
type: 'scroll',
listener: onScroll
});
// watch for toolbar resize and show/hide scroll buttons if needed
resizeObserver.observe(scrollContainerRefCurrent);
}
return function () {
if (scrollContainerRefCurrent) {
var _unbind;
(_unbind = unbind) === null || _unbind === void 0 || _unbind();
resizeObserver.unobserve(scrollContainerRefCurrent);
}
setCanScrollDebounced.cancel();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(function () {
var scrollContainerRefCurrent = scrollContainerRef.current;
if (scrollContainerRefCurrent) {
var _scrollContainerRefCu;
// reset scroll position when switching from one node with toolbar to another
// scroll to made optional as it may not be rendered in testing env
(_scrollContainerRefCu = scrollContainerRefCurrent.scrollTo) === null || _scrollContainerRefCu === void 0 || _scrollContainerRefCu.call(scrollContainerRefCurrent, {
left: 0
});
}
}, [node.type, scrollContainerRef]);
var Icon = side === 'left' ? ChevronLeftLargeIcon : ChevronRightLargeIcon;
return needScroll && (side === 'left' && canScrollToSide || side === 'right' && canScrollToSide) && /*#__PURE__*/React.createElement(Box, {
padding: "space.050"
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
,
xcss: [side === 'left' ? leftSideStyles : rightSideStyles, buttonCommonStyles]
}, /*#__PURE__*/React.createElement(IconButton, {
appearance: "subtle",
label: intl.formatMessage(side === 'left' ? messages.floatingToolbarScrollLeft : messages.floatingToolbarScrollRight),
onClick: onClick,
isDisabled: disabled
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
,
icon: function icon(iconProps) {
return /*#__PURE__*/React.createElement(Icon, {
label: iconProps.label,
size: "small"
});
},
isTooltipDisabled: false
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
,
tooltip: {
position: 'top'
}
}));
};