UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

426 lines (425 loc) 15.2 kB
"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