@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
470 lines (469 loc) • 17.2 kB
JavaScript
"use client";
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
import _extends from "@babel/runtime/helpers/esm/extends";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
const _excluded = ["page_element"];
import "core-js/modules/web.dom-collections.iterator.js";
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) { _defineProperty(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; }
import React from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import { warn, isTrue, dispatchCustomElementEvent, getPreviousSibling } from '../../shared/component-helper';
import Context from '../../shared/Context';
import Button from '../button/Button';
import { preparePageElement, isTrElement, PaginationIndicator } from './PaginationHelpers';
import PaginationContext from './PaginationContext';
export default class InfinityScroller extends React.PureComponent {
constructor(_props, context) {
var _this;
super(_props);
_this = this;
_defineProperty(this, "startup", () => {
const {
startupPage,
startup_count
} = this.context.pagination;
const startupCount = parseFloat(startup_count);
let newPageNo, skipObserver, callStartupEvent, preventWaitForDelay;
for (let i = 0; i < startupCount; ++i) {
newPageNo = startupPage + i;
skipObserver = newPageNo < startupCount;
callStartupEvent = i === 0;
preventWaitForDelay = i <= startupCount - 1;
this.getNewContent(newPageNo, {
position: 'after',
skipObserver
}, {
callStartupEvent,
preventWaitForDelay
});
}
});
_defineProperty(this, "getNewContent", function (newPageNo) {
let props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
let {
callStartupEvent = false,
preventWaitForDelay = false
} = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
const {
pageCount,
endInfinity
} = _this.context.pagination;
if (newPageNo > pageCount) {
return endInfinity();
}
const exists = _this.context.pagination.items.findIndex(obj => {
return obj.pageNumber === newPageNo;
}) > -1;
if (exists) {
return;
}
const items = _this.context.pagination.prefillItems(newPageNo, props);
_this.context.pagination.setItems(items, () => {
_this.callEventHandler(newPageNo, {
callStartupEvent,
preventWaitForDelay
});
});
});
this.hideIndicator = isTrue(context.pagination.hide_progress_indicator);
this.useLoadButton = isTrue(context.pagination.use_load_button);
this.lastElement = React.createRef();
this.callOnUnmount = [];
}
componentWillUnmount() {
clearTimeout(this._startupTimeout);
clearTimeout(this._bufferTimeout);
this.callOnUnmount.forEach(f => typeof f === 'function' && f());
}
waitForReachedTime(fn, params) {
this.callbackBuffer = this.callbackBuffer || [];
this.callbackBuffer.push({
fn,
params
});
this.callBuffer({
minTime: params.preventWaitForDelay ? -1 : this.context.pagination.minTime
});
}
callBuffer() {
let {
minTime = this.context.pagination.minTime
} = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
if (this.callbackBuffer.length === 0) {
return;
}
const diff = this._lastCall > 0 ? new Date().getTime() - this._lastCall : 0;
const waitTime = diff < minTime ? minTime : 0;
const nextTick = () => {
if (this.callbackBuffer.length > 0) {
this._lastCall = new Date().getTime();
const {
fn,
params
} = this.callbackBuffer.shift();
fn(params);
this.callBuffer({
minTime: params.preventWaitForDelay ? -1 : minTime
});
}
};
if (minTime > 0) {
clearTimeout(this._bufferTimeout);
this._bufferTimeout = setTimeout(nextTick, waitTime);
} else {
nextTick();
}
}
callEventHandler(pageNumber) {
let {
callStartupEvent = false,
preventWaitForDelay = false,
callOnEnd = false,
onDispatch = null
} = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
this.waitForReachedTime(_ref => {
let {
pageNumber,
callStartupEvent
} = _ref;
const context = this.context.pagination;
const createEvent = eventName => {
if (isNaN(pageNumber)) {
pageNumber = 1;
}
const ret = dispatchCustomElementEvent(context, eventName, _objectSpread({
pageNumber
}, context));
if (typeof onDispatch === 'function') {
onDispatch();
}
if (typeof ret === 'function') {
this.callOnUnmount.push(ret);
}
};
if (callOnEnd) {
createEvent('on_end');
} else {
if (callStartupEvent) {
createEvent('on_startup');
} else {
createEvent('on_change');
}
createEvent('on_load');
}
}, {
pageNumber,
callStartupEvent,
preventWaitForDelay
});
}
handleInfinityMarker() {
const {
children
} = this.props;
const {
lowerPage,
upperPage,
pageCount,
hasEndedInfinity,
parallelLoadCount,
current_page,
fallback_element,
marker_element,
indicator_element
} = this.context.pagination;
const Marker = () => React.createElement(InteractionMarker, {
pageNumber: upperPage,
markerElement: marker_element || fallback_element,
onVisible: pageNumber => {
let newPageNo;
for (let i = 0; i < parallelLoadCount; ++i) {
newPageNo = pageNumber + 1 + i;
this.context.pagination.onPageUpdate(() => {
this.context.pagination.setState({
upperPage: newPageNo,
skipObserver: i + 1 < parallelLoadCount
});
});
this.callEventHandler(newPageNo);
}
}
});
const LoadButton = () => React.createElement(InfinityLoadButton, {
icon: "arrow_up",
element: fallback_element,
pressed_element: React.createElement(PaginationIndicator, {
indicator_element: indicator_element || fallback_element
}),
on_click: () => {
const newPageNo = lowerPage - 1;
this.context.pagination.onPageUpdate(() => {
this.context.pagination.setState({
lowerPage: newPageNo
});
});
this.callEventHandler(newPageNo);
}
});
return React.createElement(React.Fragment, null, parseFloat(current_page) > 0 && lowerPage > 1 && React.createElement(LoadButton, null), children, !hasEndedInfinity && parseFloat(current_page) > 0 && (typeof pageCount === 'undefined' || upperPage < pageCount) && React.createElement(Marker, null), !hasEndedInfinity && !this.hideIndicator && (typeof pageCount === 'undefined' || upperPage < pageCount) && React.createElement(PaginationIndicator, {
indicator_element: indicator_element || fallback_element
}));
}
render() {
const {
items,
pageCount,
startupPage,
hasEndedInfinity,
parallelLoadCount,
placeMakerBeforeContent,
page_element,
fallback_element,
marker_element,
indicator_element,
load_button_text,
loadButton
} = this.context.pagination;
if (!(items && items.length > 0)) {
clearTimeout(this._startupTimeout);
this._startupTimeout = setTimeout(this.startup, 1);
return null;
}
if (this.context.pagination.useMarkerOnly) {
return this.handleInfinityMarker();
}
const Element = preparePageElement(page_element || React.Fragment);
return items.map((_ref2, idx) => {
let {
pageNumber,
hasContent,
content,
ref,
skipObserver,
ScrollElement
} = _ref2;
const isLastItem = idx === items.length - 1;
const Elem = hasContent && ScrollElement || Element;
const marker = hasContent && !this.useLoadButton && !skipObserver && !hasEndedInfinity && (typeof pageCount === 'undefined' || pageNumber <= pageCount) && React.createElement(InteractionMarker, {
pageNumber: pageNumber,
markerElement: marker_element || fallback_element,
onVisible: pageNumber => {
let newPageNo;
for (let i = 0; i < parallelLoadCount; ++i) {
newPageNo = pageNumber + 1 + i;
this.getNewContent(newPageNo, {
position: 'after',
skipObserver: i + 1 < parallelLoadCount
});
}
}
});
const showIndicator = (parallelLoadCount > 1 && idx > 0 ? isLastItem : true) && !hasContent && !this.hideIndicator;
return React.createElement(Elem, {
key: pageNumber,
ref: ref
}, hasContent && startupPage > 1 && pageNumber > 1 && pageNumber <= startupPage && React.createElement(InfinityLoadButton, {
element: typeof loadButton === 'function' ? loadButton : fallback_element,
icon: "arrow_up",
text: load_button_text !== null && load_button_text !== void 0 ? load_button_text : loadButton === null || loadButton === void 0 ? void 0 : loadButton.text,
icon_position: loadButton === null || loadButton === void 0 ? void 0 : loadButton.iconPosition,
on_click: event => this.getNewContent(pageNumber - 1, {
position: 'before',
skipObserver: true,
event
})
}), placeMakerBeforeContent && marker, content, !placeMakerBeforeContent && marker, showIndicator && React.createElement(PaginationIndicator, {
indicator_element: indicator_element || fallback_element
}), hasContent && this.useLoadButton && isLastItem && (typeof pageCount === 'undefined' || pageNumber < pageCount) && React.createElement(InfinityLoadButton, {
element: typeof loadButton === 'function' ? loadButton : fallback_element,
text: load_button_text !== null && load_button_text !== void 0 ? load_button_text : loadButton === null || loadButton === void 0 ? void 0 : loadButton.text,
icon_position: loadButton === null || loadButton === void 0 ? void 0 : loadButton.iconPosition,
icon: "arrow_down",
on_click: event => this.getNewContent(pageNumber + 1, {
position: 'after',
skipObserver: true,
ScrollElement: props => hasContent && React.createElement(ScrollToElement, _extends({
page_element: page_element
}, props)),
event
})
}));
});
}
}
_defineProperty(InfinityScroller, "contextType", PaginationContext);
_defineProperty(InfinityScroller, "defaultProps", {
children: null
});
process.env.NODE_ENV !== "production" ? InfinityScroller.propTypes = {
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func])
} : void 0;
class InteractionMarker extends React.PureComponent {
constructor(props) {
super(props);
_defineProperty(this, "state", {
isConnected: false
});
_defineProperty(this, "callReady", () => {
var _this$intersectionObs;
(_this$intersectionObs = this.intersectionObserver) === null || _this$intersectionObs === void 0 ? void 0 : _this$intersectionObs.disconnect();
this.intersectionObserver = null;
clearTimeout(this._readyTimeout);
this._readyTimeout = setTimeout(() => {
if (this._isMounted) {
this.setState({
isConnected: true
});
}
this.props.onVisible(this.props.pageNumber);
}, 1);
});
if (typeof props.markerElement === 'function') {
warn('Pagination: Please use a string or React element e.g. marker_element="tr"');
}
this._ref = React.createRef();
if (typeof IntersectionObserver !== 'undefined') {
this.intersectionObserver = new IntersectionObserver(entries => {
const [{
isIntersecting
}] = entries;
if (isIntersecting) {
this.callReady();
}
});
} else {
warn('Pagination is missing IntersectionObserver supported!');
}
}
componentDidMount() {
if (this._ref.current) {
var _this$intersectionObs2;
this._isMounted = true;
(_this$intersectionObs2 = this.intersectionObserver) === null || _this$intersectionObs2 === void 0 ? void 0 : _this$intersectionObs2.observe(this._ref.current);
}
}
componentWillUnmount() {
this._isMounted = false;
if (this.intersectionObserver) {
clearTimeout(this._readyTimeout);
this.intersectionObserver.disconnect();
}
}
getContentHeight() {
let height = 0;
try {
const sibling = getPreviousSibling('dnb-table', this._ref.current);
height = parseFloat(window.getComputedStyle(sibling.querySelector('tbody')).height);
} catch (e) {}
return height;
}
render() {
const {
markerElement
} = this.props;
if (this.state.isConnected || !this.intersectionObserver) {
return null;
}
const Element = markerElement && isTrElement(markerElement) ? 'tr' : 'div';
const ElementChild = markerElement && isTrElement(markerElement) ? 'td' : 'div';
return React.createElement(Element, {
className: "dnb-pagination__marker dnb-table--ignore"
}, React.createElement(ElementChild, {
className: "dnb-pagination__marker__inner",
ref: this._ref
}));
}
}
_defineProperty(InteractionMarker, "defaultProps", {
markerElement: null
});
process.env.NODE_ENV !== "production" ? InteractionMarker.propTypes = {
pageNumber: PropTypes.number.isRequired,
onVisible: PropTypes.func.isRequired,
markerElement: PropTypes.oneOfType([PropTypes.object, PropTypes.node, PropTypes.func, PropTypes.string])
} : void 0;
export class InfinityLoadButton extends React.PureComponent {
constructor() {
super(...arguments);
_defineProperty(this, "state", {
isPressed: false
});
_defineProperty(this, "onClickHandler", e => {
this.setState({
isPressed: true
});
if (typeof this.props.on_click === 'function') {
this.props.on_click(e);
}
});
}
render() {
const {
element,
icon,
text,
icon_position
} = this.props;
const Element = element;
const ElementChild = isTrElement(Element) ? 'td' : 'div';
return this.state.isPressed ? this.props.pressed_element : React.createElement(Element, null, React.createElement(ElementChild, {
className: "dnb-pagination__loadbar"
}, React.createElement(Button, {
size: "medium",
icon: icon,
icon_position: icon_position,
text: text || this.context.translation.Pagination.load_button_text,
variant: "secondary",
on_click: this.onClickHandler
})));
}
}
_defineProperty(InfinityLoadButton, "contextType", Context);
_defineProperty(InfinityLoadButton, "defaultProps", {
element: 'div',
pressed_element: null,
icon: 'arrow_down',
text: null,
icon_position: 'left'
});
process.env.NODE_ENV !== "production" ? InfinityLoadButton.propTypes = {
element: PropTypes.oneOfType([PropTypes.object, PropTypes.node, PropTypes.func, PropTypes.string]),
pressed_element: PropTypes.oneOfType([PropTypes.object, PropTypes.node, PropTypes.func]),
icon: PropTypes.string.isRequired,
on_click: PropTypes.func.isRequired,
text: PropTypes.string,
icon_position: PropTypes.string
} : void 0;
class ScrollToElement extends React.PureComponent {
componentDidMount() {
const elem = ReactDOM.findDOMNode(this);
this.scrollToPage(elem);
}
scrollToPage(element) {
if (element && typeof element.scrollIntoView === 'function') {
element.scrollIntoView({
block: 'nearest',
behavior: 'smooth'
});
}
}
render() {
const _this$props = this.props,
{
page_element
} = _this$props,
props = _objectWithoutProperties(_this$props, _excluded);
const Element = preparePageElement(page_element || React.Fragment);
return React.createElement(Element, props);
}
}
_defineProperty(ScrollToElement, "defaultProps", {
page_element: null
});
process.env.NODE_ENV !== "production" ? ScrollToElement.propTypes = {
page_element: PropTypes.oneOfType([PropTypes.object, PropTypes.node, PropTypes.func, PropTypes.string])
} : void 0;
InfinityScroller._supportsSpacingProps = true;
//# sourceMappingURL=PaginationInfinity.js.map