@atlaskit/editor-plugin-table
Version:
Table plugin for the @atlaskit/editor
146 lines (143 loc) • 7.23 kB
JavaScript
import React, { useEffect, useMemo, useRef } from 'react';
import rafSchedule from 'raf-schd';
import { createPortal } from 'react-dom';
import { akEditorTableCellOnStickyHeaderZIndex } from '@atlaskit/editor-shared-styles';
import { TableCssClassName as ClassName } from '../../types';
import { insertColumnButtonOffset } from '../common-styles';
var BUTTON_WIDTH = 20;
export var calcLeftPos = function calcLeftPos(_ref) {
var buttonWidth = _ref.buttonWidth,
cellRectLeft = _ref.cellRectLeft,
cellRefWidth = _ref.cellRefWidth,
offset = _ref.offset;
return cellRectLeft + cellRefWidth - buttonWidth - offset;
};
export var calcObserverTargetMargin = function calcObserverTargetMargin(tableWrapper, fixedButtonRefCurrent) {
var tableWrapperRect = tableWrapper.getBoundingClientRect();
var fixedButtonRect = fixedButtonRefCurrent.getBoundingClientRect();
var scrollLeft = tableWrapper.scrollLeft;
return fixedButtonRect.left - tableWrapperRect.left + scrollLeft;
};
export var FixedButton = function FixedButton(_ref2) {
var children = _ref2.children,
isContextualMenuOpen = _ref2.isContextualMenuOpen,
mountTo = _ref2.mountTo,
offset = _ref2.offset,
stickyHeader = _ref2.stickyHeader,
tableWrapper = _ref2.tableWrapper,
targetCellPosition = _ref2.targetCellPosition,
targetCellRef = _ref2.targetCellRef;
var fixedButtonRef = useRef(null);
var observerTargetRef = useRef(null);
// Using refs here rather than state to prevent heaps of renders on scroll
var scrollDataRef = useRef(0);
var leftPosDataRef = useRef(0);
useEffect(function () {
var observerTargetRefCurrent = observerTargetRef.current;
var fixedButtonRefCurrent = fixedButtonRef.current;
if (fixedButtonRefCurrent && observerTargetRefCurrent) {
scrollDataRef.current = tableWrapper.scrollLeft;
leftPosDataRef.current = 0;
// Hide the button initially in case there's a flash of the button being
// outside the table before the Intersection Observer fires
fixedButtonRefCurrent.style.visibility = 'hidden';
var margin = calcObserverTargetMargin(tableWrapper, fixedButtonRefCurrent);
// Much more simple and predictable to add this margin to the observer target
// rather than using it to calculate the rootMargin values
observerTargetRefCurrent.style.marginLeft = "".concat(margin, "px");
var observer = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
fixedButtonRefCurrent.style.visibility = 'visible';
} else {
fixedButtonRefCurrent.style.visibility = 'hidden';
}
});
}, {
root: tableWrapper,
rootMargin: "0px ".concat(insertColumnButtonOffset, "px 0px 0px"),
threshold: 1
});
var handleScroll = rafSchedule(function (event) {
if (fixedButtonRef.current) {
var delta = event.target.scrollLeft - scrollDataRef.current;
var style = "translateX(".concat(leftPosDataRef.current - delta, "px)");
fixedButtonRef.current.style.transform = style;
scrollDataRef.current = event.target.scrollLeft;
leftPosDataRef.current = leftPosDataRef.current - delta;
}
});
observer.observe(observerTargetRefCurrent);
// Ignored via go/ees005
// eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
tableWrapper.addEventListener('scroll', handleScroll);
return function () {
// Ignored via go/ees005
// eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
tableWrapper.removeEventListener('scroll', handleScroll);
fixedButtonRefCurrent.style.transform = '';
observer.unobserve(observerTargetRefCurrent);
};
}
}, [fixedButtonRef, observerTargetRef, tableWrapper, targetCellPosition, targetCellRef, isContextualMenuOpen]);
var fixedButtonTop = 0;
var containerLeft = useMemo(function () {
var container = targetCellRef.closest('[data-editor-container="true"]');
return (container === null || container === void 0 ? void 0 : container.getBoundingClientRect().left) || 0;
}, [targetCellRef]);
var left = useMemo(function () {
var targetCellRect = targetCellRef.getBoundingClientRect();
var baseLeft = calcLeftPos({
buttonWidth: BUTTON_WIDTH,
cellRectLeft: targetCellRect.left,
cellRefWidth: targetCellRef.clientWidth,
offset: offset
});
return baseLeft - containerLeft;
}, [containerLeft, targetCellRef, offset]);
// Using a portal here to ensure wrapperRef has the tableWrapper as an
// ancestor. This is required to make the Intersection Observer work.
return /*#__PURE__*/createPortal(
/*#__PURE__*/
// Using observerTargetRef here for our Intersection Observer. There is issues
// getting the observer to work just using the fixedButtonRef, possible due
// to using position fixed on this Element, or possibly due to its position
// being changed on scroll.
React.createElement("div", {
ref: observerTargetRef,
style: {
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
position: 'absolute',
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
top: "var(--ds-space-0, 0px)",
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
left: "var(--ds-space-0, 0px)",
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
width: "var(--ds-space-250, 20px)",
// BUTTON_WIDTH
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
height: "var(--ds-space-250, 20px)" // BUTTON_WIDTH
}
}, /*#__PURE__*/React.createElement("div", {
ref: fixedButtonRef,
style: {
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
position: 'fixed',
// eslint-disable-next-line @atlaskit/design-system/ensure-design-token-usage/preview
top: fixedButtonTop + stickyHeader.padding + offset * 2,
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
zIndex: akEditorTableCellOnStickyHeaderZIndex,
// eslint-disable-next-line @atlaskit/design-system/ensure-design-token-usage/preview
left: left,
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
width: "var(--ds-space-250, 20px)",
// BUTTON_WIDTH
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
height: "var(--ds-space-250, 20px)" // BUTTON_WIDTH
}
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
,
className: ClassName.CONTEXTUAL_MENU_BUTTON_FIXED
}, children)), mountTo);
};
export default FixedButton;