UNPKG

@atlaskit/dynamic-table

Version:

A dynamic table displays rows of data with built-in pagination, sorting, and re-ordering functionality.

177 lines (176 loc) 7.08 kB
import _defineProperty from "@babel/runtime/helpers/defineProperty"; /* eslint-disable @repo/internal/dom-events/no-unsafe-event-listeners */ import React from 'react'; import Spinner from '@atlaskit/spinner'; import { LARGE, LOADING_CONTENTS_OPACITY } from '../internal/constants'; import { Container, SpinnerBackdrop, SpinnerContainer } from '../styled/loading-container-advanced'; export default class LoadingContainerAdvanced extends React.Component { constructor(...args) { super(...args); _defineProperty(this, "spinnerRef", /*#__PURE__*/React.createRef()); _defineProperty(this, "containerRef", /*#__PURE__*/React.createRef()); _defineProperty(this, "componentDidMount", () => { if (this.props.isLoading && this.hasTargetNode()) { this.attachListeners(); this.updateTargetAppearance(); this.updateSpinnerPosition(); } }); _defineProperty(this, "UNSAFE_componentWillReceiveProps", nextProps => { if (!nextProps.isLoading || !this.hasTargetNode(nextProps)) { this.detachListeners(); } else if (!this.props.isLoading) { this.attachListeners(); } }); _defineProperty(this, "componentDidUpdate", () => { if (this.hasTargetNode()) { this.updateTargetAppearance(); if (this.props.isLoading) { this.updateSpinnerPosition(); } } }); _defineProperty(this, "componentWillUnmount", () => { this.detachListeners(); }); _defineProperty(this, "getTargetNode", (nextProps = this.props) => { const { targetRef } = nextProps; const target = targetRef === null || targetRef === void 0 ? void 0 : targetRef(); return target || this.containerRef.current; }); _defineProperty(this, "hasTargetNode", nextProps => !!this.getTargetNode(nextProps)); _defineProperty(this, "isVerticallyVisible", (elementRect, viewportHeight) => { const { top, bottom } = elementRect; if (bottom <= 0) { return false; } return top < viewportHeight; }); _defineProperty(this, "isFullyVerticallyVisible", (elementRect, viewportHeight) => { const { top, bottom } = elementRect; return top >= 0 && bottom <= viewportHeight; }); _defineProperty(this, "handleResize", () => { this.updateSpinnerPosition(); }); _defineProperty(this, "handleScroll", () => { this.updateSpinnerPosition(); }); _defineProperty(this, "translateSpinner", (spinnerNode, transformY, isFixed) => { spinnerNode.style.position = isFixed ? 'fixed' : ''; spinnerNode.style.transform = transformY !== 0 ? `translate3d(0, ${transformY}px, 0)` : ''; }); _defineProperty(this, "updateTargetAppearance", () => { const targetNode = this.getTargetNode(); const { isLoading, contentsOpacity } = this.props; if (targetNode && targetNode.style && typeof targetNode.style === 'object') { targetNode.style.pointerEvents = isLoading ? 'none' : ''; targetNode.style.opacity = isLoading ? contentsOpacity.toString() : ''; } }); } attachListeners() { window.addEventListener('scroll', this.handleScroll); window.addEventListener('resize', this.handleResize); } detachListeners() { window.removeEventListener('scroll', this.handleScroll); window.removeEventListener('resize', this.handleResize); } updateSpinnerPosition() { var _this$spinnerRef, _this$containerRef; const viewportHeight = window.innerHeight; const targetNode = this.getTargetNode(); const spinnerNode = (_this$spinnerRef = this.spinnerRef) === null || _this$spinnerRef === void 0 ? void 0 : _this$spinnerRef.current; if (!targetNode || typeof targetNode.getBoundingClientRect !== 'function' || !spinnerNode) { return; } const targetRect = targetNode.getBoundingClientRect(); const spinnerRect = spinnerNode.getBoundingClientRect(); const spinnerHeight = spinnerRect.height; const isInViewport = this.isVerticallyVisible(targetRect, viewportHeight); const { top, bottom, height } = targetRect; if (isInViewport) { // The spinner may follow the element only if there is enough space: // Let's say the element can fit at least three spinners (vertically) const canFollow = height >= spinnerHeight * 3; if (canFollow && !this.isFullyVerticallyVisible(targetRect, viewportHeight)) { if (top >= 0) { // Only the head of the element is visible const viewportSpaceTakenByElement = viewportHeight - top; const diff = viewportSpaceTakenByElement / 2 + top - spinnerHeight / 2; const y = viewportSpaceTakenByElement < spinnerHeight * 3 ? top + spinnerHeight : diff; this.translateSpinner(spinnerNode, y, true); } else if (top < 0 && bottom > viewportHeight) { // The element takes all viewport, nor its head nor tail are visible const y = viewportHeight / 2 - spinnerHeight / 2; this.translateSpinner(spinnerNode, y, true); } else { // Only the tail of the element is visible const diff = bottom / 2 - spinnerHeight / 2; const y = diff < spinnerHeight ? diff - (spinnerHeight - diff) : diff; this.translateSpinner(spinnerNode, y, true); } return; } } else { // If both the element and the spinner are off screen - quit if (!this.isVerticallyVisible(spinnerRect, viewportHeight)) { return; } } // Three options here: // 1) the element is fully visible // 2) the element is too small for the spinner to follow // 3) the spinner might still be visible while the element isn't const containerNode = (_this$containerRef = this.containerRef) === null || _this$containerRef === void 0 ? void 0 : _this$containerRef.current; if (containerNode && typeof containerNode.getBoundingClientRect === 'function') { const thisTop = containerNode.getBoundingClientRect().top; const y = (top - thisTop) / 2; this.translateSpinner(spinnerNode, y, false); } } render() { const { children, isLoading, spinnerSize, testId, loadingLabel } = this.props; return /*#__PURE__*/React.createElement(Container, { testId: testId && `${testId}--loading--container--advanced`, ref: this.containerRef }, children, isLoading && /*#__PURE__*/React.createElement(SpinnerBackdrop, { testId: testId }, /*#__PURE__*/React.createElement(SpinnerContainer, { ref: this.spinnerRef }, /*#__PURE__*/React.createElement(Spinner, { size: spinnerSize, testId: testId && `${testId}--loadingSpinner`, label: loadingLabel })))); } } _defineProperty(LoadingContainerAdvanced, "defaultProps", { isLoading: true, spinnerSize: LARGE, contentsOpacity: `var(--ds-opacity-loading, ${`${LOADING_CONTENTS_OPACITY}`})`, loadingLabel: 'Loading table' });