UNPKG

@atlaskit/editor-plugin-floating-toolbar

Version:

Floating toolbar plugin for @atlaskit/editor-core

150 lines (149 loc) 6.59 kB
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' } })); };