@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
426 lines (425 loc) • 15.2 kB
JavaScript
"use client";
import _JSON$parse from "core-js-pure/stable/json/parse.js";
import _pushInstanceProperty from "core-js-pure/stable/instance/push.js";
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
import { useIsomorphicLayoutEffect } from "../../shared/helpers/useIsomorphicLayoutEffect.js";
import Context from "../../shared/Context.js";
import { dispatchCustomElementEvent } from "../../shared/component-helper.js";
import { ContentObject } from "./PaginationHelpers.js";
import PaginationContext from "./PaginationContext.js";
import { jsx as _jsx } from "react/jsx-runtime";
const PaginationProvider = props => {
const sharedContext = useContext(Context);
const computeDerived = useCallback(() => {
const state = {};
if (props.pageCount != null) {
state.pageCountInternal = parseFloat(props.pageCount) || 1;
}
state.parallelLoadCount = parseFloat(props.parallelLoadCount) || 1;
state.minTime = parseFloat(props.minWaitTime) || 0;
state.placeMakerBeforeContent = props.placeMarkerBeforeContent;
return state;
}, [props.pageCount, props.parallelLoadCount, props.minWaitTime, props.placeMarkerBeforeContent]);
const [items, setItemsState] = useState(() => {
if (typeof props.items === 'string' && props.items[0] === '[') {
return _JSON$parse(props.items);
} else if (Array.isArray(props.items)) {
return props.items;
}
return [];
});
const [isLoading, setIsLoading] = useState(false);
const [hasEndedInfinity, setHasEndedInfinity] = useState(false);
const [currentPageInternal, setCurrentPageInternal] = useState(() => {
if (props.currentPage != null) {
return parseFloat(props.currentPage) || 1;
}
return undefined;
});
const [startupPage, setStartupPage] = useState(() => {
return parseFloat(props.startupPage) || parseFloat(props.currentPage) || (props.currentPage != null ? parseFloat(props.currentPage) || 1 : undefined);
});
const [lowerPage, setLowerPage] = useState(() => {
if (props.useMarkerOnly) {
return parseFloat(props.startupPage) || parseFloat(props.currentPage) || 1;
}
return undefined;
});
const [upperPage, setUpperPage] = useState(() => {
if (props.useMarkerOnly) {
const sp = parseFloat(props.startupPage) || parseFloat(props.currentPage) || 1;
return sp + (parseFloat(props.startupCount) || 1) - 1 || 1;
}
return undefined;
});
const isMountedRef = useRef(false);
const updateStackRef = useRef([]);
const rerenderTimeoutRef = useRef(undefined);
const resetContentTimeoutRef = useRef(undefined);
const resetInfinityTimeoutRef = useRef(undefined);
const callOnPageUpdateTimeoutRef = useRef(undefined);
const pendingCallOnPageUpdateRef = useRef(false);
const pendingSetItemsCbRef = useRef(null);
const pendingSetStateCbRef = useRef(null);
const itemsRef = useRef(items);
const currentPageInternalRef = useRef(currentPageInternal);
const startupPageRef = useRef(startupPage);
itemsRef.current = items;
currentPageInternalRef.current = currentPageInternal;
startupPageRef.current = startupPage;
const propsRef = useRef(props);
propsRef.current = props;
const derived = computeDerived();
const prevPropsRef = useRef({
currentPage: props.currentPage,
internalContent: props.internalContent
});
const callOnPageUpdate = useCallback(() => {
if (Array.isArray(updateStackRef.current)) {
updateStackRef.current.forEach(cb => {
if (typeof cb === 'function') {
cb();
}
});
updateStackRef.current = [];
}
}, []);
const onPageUpdate = useCallback(fn => {
var _context;
updateStackRef.current = updateStackRef.current || [];
_pushInstanceProperty(_context = updateStackRef.current).call(_context, fn);
}, []);
const prefillItems = useCallback((pageNumber, itemProps = {}, existingItems = itemsRef.current) => {
const position = itemProps.position || (pageNumber < currentPageInternalRef.current ? 'before' : 'after');
if (isNaN(pageNumber)) {
pageNumber = 1;
}
const exists = existingItems.findIndex(({
pageNumber: p
}) => p === pageNumber) > -1;
if (exists) {
return existingItems;
}
const obj = {
pageNumber,
position,
skipObserver: false,
...itemProps
};
switch (position) {
case 'before':
return [new ContentObject(obj), ...existingItems];
case 'after':
return [...existingItems, new ContentObject(obj)];
}
return undefined;
}, []);
const setContent = useCallback((newContent, content = null, position = null) => {
if (!Array.isArray(newContent) && content) {
newContent = [newContent, content];
}
const pageNumber = parseFloat(newContent[0]) || 1;
newContent = newContent[1];
if (typeof newContent === 'function') {
content = newContent();
} else if (React.isValidElement(newContent)) {
content = newContent;
}
if (content) {
let itemToPrepare = itemsRef.current.find(({
pageNumber: p
}) => p === pageNumber);
let newItems = null;
if (!itemToPrepare) {
newItems = prefillItems(pageNumber, {
position
});
itemToPrepare = newItems.find(({
pageNumber: p
}) => p === pageNumber);
}
if (itemToPrepare.content) {
itemToPrepare.update(content);
} else {
itemToPrepare.insert(content);
}
const updatedItems = [...(newItems || itemsRef.current)];
setItemsState(updatedItems);
setCurrentPageInternal(pageNumber);
pendingCallOnPageUpdateRef.current = true;
}
}, [prefillItems]);
const resetContent = useCallback(() => {
clearTimeout(resetContentTimeoutRef.current);
resetContentTimeoutRef.current = setTimeout(() => {
setItemsState([]);
}, 10);
}, []);
const resetInfinity = useCallback((pageNumber = startupPageRef.current) => {
const newLowerPage = pageNumber;
const newUpperPage = pageNumber + parseFloat(propsRef.current.startupCount) - 1;
const newCurrentPageInternal = pageNumber;
setItemsState([]);
setHasEndedInfinity(true);
setLowerPage(newLowerPage);
setUpperPage(newUpperPage);
setCurrentPageInternal(newCurrentPageInternal);
setHasEndedInfinity(false);
}, []);
const endInfinityDispatchRef = useRef(false);
const endInfinityHandler = useCallback(() => {
setHasEndedInfinity(true);
endInfinityDispatchRef.current = true;
}, []);
const setItems = useCallback((newItems, cb) => {
setItemsState(newItems);
if (typeof cb === 'function') {
pendingSetItemsCbRef.current = cb;
}
}, []);
const setStateHandler = useCallback((state, cb) => {
if ('items' in state) {
setItemsState(state.items);
}
if ('isLoading' in state) {
setIsLoading(state.isLoading);
}
if ('hasEndedInfinity' in state) {
setHasEndedInfinity(state.hasEndedInfinity);
}
if ('currentPageInternal' in state) {
setCurrentPageInternal(state.currentPageInternal);
}
if ('startupPage' in state) {
setStartupPage(state.startupPage);
}
if ('lowerPage' in state) {
setLowerPage(state.lowerPage);
}
if ('upperPage' in state) {
setUpperPage(state.upperPage);
}
if (typeof cb === 'function') {
pendingSetStateCbRef.current = cb;
}
}, []);
const updatePageContent = useCallback(pageNumber => {
const pn = pageNumber !== undefined ? pageNumber : currentPageInternalRef.current;
let potentialElement = propsRef.current.internalContent;
if (typeof propsRef.current.internalContent === 'function') {
potentialElement = propsRef.current.internalContent({
updatePageContent,
setContent,
resetContent,
resetInfinity,
endInfinity: endInfinityHandler,
setItems,
prefillItems,
setState: setStateHandler,
onPageUpdate,
...propsRef.current,
items: itemsRef.current,
currentPageInternal: currentPageInternalRef.current,
startupPage: startupPageRef.current,
isLoading,
hasEndedInfinity,
lowerPage,
upperPage,
pageNumber: pn,
page: pn
});
}
if (potentialElement && React.isValidElement(potentialElement)) {
setContent([pn, potentialElement]);
}
}, [setContent, resetContent, resetInfinity, endInfinityHandler, setItems, prefillItems, setStateHandler, onPageUpdate, isLoading, hasEndedInfinity, lowerPage, upperPage]);
useIsomorphicLayoutEffect(() => {
if (hasEndedInfinity && endInfinityDispatchRef.current) {
endInfinityDispatchRef.current = false;
const pageNumber = currentPageInternalRef.current + 1;
dispatchCustomElementEvent({
props: propsRef.current
}, 'onEnd', {
pageNumber,
updatePageContent,
setContent,
resetContent,
resetInfinity,
endInfinity: endInfinityHandler,
setItems,
prefillItems,
setState: setStateHandler,
onPageUpdate,
...propsRef.current,
items: itemsRef.current,
currentPageInternal: currentPageInternalRef.current,
startupPage: startupPageRef.current
});
}
}, [hasEndedInfinity, updatePageContent, setContent, resetContent, resetInfinity, endInfinityHandler, setItems, prefillItems, setStateHandler, onPageUpdate]);
useIsomorphicLayoutEffect(() => {
if (pendingCallOnPageUpdateRef.current) {
pendingCallOnPageUpdateRef.current = false;
callOnPageUpdate();
}
if (pendingSetItemsCbRef.current) {
const cb = pendingSetItemsCbRef.current;
pendingSetItemsCbRef.current = null;
cb();
}
if (pendingSetStateCbRef.current) {
const cb = pendingSetStateCbRef.current;
pendingSetStateCbRef.current = null;
cb();
}
});
useIsomorphicLayoutEffect(() => {
if (typeof props.items === 'string' && props.items[0] === '[') {
setItemsState(_JSON$parse(props.items));
} else if (Array.isArray(props.items)) {
setItemsState(props.items);
}
}, [props.items]);
useIsomorphicLayoutEffect(() => {
if (props.currentPage != null && typeof currentPageInternalRef.current === 'undefined') {
setCurrentPageInternal(parseFloat(props.currentPage) || 1);
}
}, [props.currentPage]);
useIsomorphicLayoutEffect(() => {
if (typeof startupPageRef.current !== 'number') {
const derived = parseFloat(props.startupPage) || parseFloat(props.currentPage) || currentPageInternalRef.current;
if (typeof derived === 'number' && !isNaN(derived)) {
setStartupPage(derived);
}
}
}, [props.startupPage, props.currentPage]);
useIsomorphicLayoutEffect(() => {
if (props.resetContentHandler === true) {
setItemsState([]);
}
}, [props.resetContentHandler]);
useIsomorphicLayoutEffect(() => {
if (props.useMarkerOnly && props.resetPaginationHandler === true) {
setLowerPage(undefined);
setUpperPage(undefined);
}
}, [props.useMarkerOnly, props.resetPaginationHandler]);
useIsomorphicLayoutEffect(() => {
if (props.useMarkerOnly) {
const sp = startupPageRef.current || parseFloat(props.currentPage) || 1;
setLowerPage(prev => {
if (typeof prev === 'undefined') {
return sp;
}
const cur = parseFloat(props.currentPage);
if (!isNaN(cur) && cur < prev) {
return cur;
}
return prev;
});
setUpperPage(prev => {
if (typeof prev === 'undefined') {
return sp + (parseFloat(props.startupCount) || 1) - 1 || 1;
}
return prev;
});
}
}, [props.useMarkerOnly, props.currentPage, props.startupCount]);
useEffect(() => {
if (props.rerender) {
const rerenderFn = ({
current: store
}) => {
if (store && store.pageNumber > 0) {
clearTimeout(rerenderTimeoutRef.current);
rerenderTimeoutRef.current = setTimeout(() => setContent(store.pageNumber, store.content), 1);
}
};
props.rerender.current = rerenderFn;
}
}, [props.rerender, setContent]);
useIsomorphicLayoutEffect(() => {
const {
setContentHandler,
resetContentHandler,
resetPaginationHandler,
endInfinityHandler: endInfinityHandlerProp
} = props;
if (typeof setContentHandler === 'function') {
setContentHandler(setContent);
}
if (typeof resetContentHandler === 'function') {
resetContentHandler(resetContent);
}
if (typeof resetPaginationHandler === 'function') {
resetPaginationHandler(resetInfinity);
}
if (typeof endInfinityHandlerProp === 'function') {
endInfinityHandlerProp(endInfinityHandler);
}
if (props.store && props.store.current) {
const store = props.store.current;
setContent(store.pageNumber, store.content);
}
isMountedRef.current = true;
updatePageContent(startupPageRef.current || currentPageInternalRef.current);
return () => {
clearTimeout(rerenderTimeoutRef.current);
clearTimeout(resetContentTimeoutRef.current);
clearTimeout(resetInfinityTimeoutRef.current);
clearTimeout(callOnPageUpdateTimeoutRef.current);
isMountedRef.current = false;
};
}, []);
useIsomorphicLayoutEffect(() => {
const prevCurrentPage = prevPropsRef.current.currentPage;
const prevInternalContent = prevPropsRef.current.internalContent;
if (props.currentPage !== prevCurrentPage) {
const newCurrentPage = parseFloat(props.currentPage);
setCurrentPageInternal(newCurrentPage);
updatePageContent(newCurrentPage);
} else if (props.internalContent !== prevInternalContent) {
updatePageContent();
}
prevPropsRef.current = {
currentPage: props.currentPage,
internalContent: props.internalContent
};
});
useEffect(() => {
if (props.useMarkerOnly) {
clearTimeout(callOnPageUpdateTimeoutRef.current);
callOnPageUpdateTimeoutRef.current = setTimeout(callOnPageUpdate, 1);
}
});
const contextValue = useMemo(() => ({
...sharedContext,
pagination: {
updatePageContent,
setContent,
resetContent,
resetInfinity,
endInfinity: endInfinityHandler,
setItems,
prefillItems,
setState: setStateHandler,
onPageUpdate,
...props,
...derived,
items,
isLoading,
hasEndedInfinity,
currentPageInternal,
startupPage,
lowerPage,
upperPage
}
}), [sharedContext, updatePageContent, setContent, resetContent, resetInfinity, endInfinityHandler, setItems, prefillItems, setStateHandler, onPageUpdate, props, derived, items, isLoading, hasEndedInfinity, currentPageInternal, startupPage, lowerPage, upperPage]);
return _jsx(PaginationContext, {
value: contextValue,
children: props.children
});
};
const MemoizedPaginationProvider = React.memo(PaginationProvider);
export default MemoizedPaginationProvider;
//# sourceMappingURL=PaginationProvider.js.map