UNPKG

@findify/react-components

Version:
143 lines (122 loc) 4.36 kB
import { useCallback, useEffect, useMemo, useRef } from 'react'; import { List, Map } from 'immutable'; import { useItems } from '@findify/react-connect'; import { emit } from "./emmiter"; var hasRange = function hasRange(ranges, offset) { return !!ranges.find(function (r) { return r.get('from') === offset; }); }; var createRange = function createRange(meta) { return Map({ from: meta.get('offset'), to: meta.get('offset') + meta.get('limit') }); }; var updateItems = function updateItems(_ref, nextItems, meta) { var ranges = _ref.ranges, items = _ref.items; var append = ranges.find(function (r) { return r.get('from') < meta.get('offset'); }); var newRange = createRange(meta); var _items = nextItems.filter(function (i) { return !items.includes(i); }); return { ranges: append ? ranges.push(newRange) : ranges.insert(0, newRange), items: append ? items.concat(_items) : _items.concat(items), meta: meta }; }; var initialState = { items: List(), ranges: List(), meta: Map() }; export default (function () { var offset = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 300; var _useItems = useItems(), items = _useItems.items, meta = _useItems.meta, update = _useItems.update, config = _useItems.config; var container = useRef(null); var pending = useRef(true); var state = useRef(initialState); var autoLoad = useRef(config.getIn(['pagination', 'autoLoadTimes']) || 0); var onLoadNext = useCallback(function (e) { e.preventDefault(); pending.current = true; return update('offset', state.current.ranges.last().get('to')); }, [meta]); var onLoadPrev = useCallback(function (e) { e.preventDefault(); pending.current = true; return update('offset', state.current.ranges.first().get('from') - meta.get('limit')); }, [meta]); var trackPosition = function trackPosition() { if (pending.current || !autoLoad.current || !state.current.items.size || state.current.ranges.last().get('to') >= state.current.meta.get('total')) return; Promise.resolve().then(function () { var _container$current; if (!container.current || ((_container$current = container.current) === null || _container$current === void 0 ? void 0 : _container$current.clientHeight) === 0) return; var _container$current$ge = container.current.getBoundingClientRect(), bottom = _container$current$ge.bottom; var height = window.innerHeight || document.documentElement.clientHeight; var inView = bottom - height <= offset; if (!inView) return; autoLoad.current -= 1; pending.current = true; update('offset', state.current.ranges.last().get('to')); }); }; useEffect(function () { if (container.current === null) return; window.addEventListener('scroll', trackPosition, true); pending.current = false; return function () { return window.removeEventListener('scroll', trackPosition); }; }, [container]); /** * Handle items change */ var getUpdate = function getUpdate() { /** * Reset refs if update has come from outside of this component */ if (!pending.current) { var _update = updateItems(initialState, items, meta); autoLoad.current = config.getIn(['pagination', 'autoLoadTimes']) || 0; pending.current = true; /** Scroll to top on query change */ emit('scrollTop'); pending.current = false; state.current = _update; return _update; } /** * Check the new ranges and if there is difference update state */ if (!hasRange(state.current.ranges, meta.get('offset'))) { var _update2 = updateItems(state.current, items, meta); pending.current = false; state.current = _update2; return _update2; } return state.current; }; return useMemo(function () { var update = getUpdate(); var firstRange = update.ranges.first(); var lastRange = update.ranges.last(); return { container: container, onLoadNext: onLoadNext, onLoadPrev: onLoadPrev, items: update.items, displayPrevButton: firstRange && firstRange.get('from') > 0, displayNextButton: lastRange && lastRange.get('to') < meta.get('total') && !autoLoad.current }; }, [items]); });