UNPKG

phx-react

Version:

PHX REACT

137 lines 5.5 kB
'use client'; import { useEffect, useRef } from 'react'; import { pageTrackerStore } from './page-tracker-store'; const DEBUG = false; const useStrictModeDetector = () => { const counterRef = useRef(0); const isStrictMode = useRef(false); const detectorHandler = () => { counterRef.current++; if (isStrictMode.current) { return counterRef.current % 2 === 0; } return false; }; return { initStrictModeDetector: () => { counterRef.current++; if (counterRef.current === 2) { isStrictMode.current = true; } }, strictModeDetector: () => { return detectorHandler(); }, }; }; const debugLog = (message) => { if (DEBUG) { console.debug(`[DEBUG PAGE CHANGE] ${message}`); } }; const initHistoryState = () => { const defaultData = { __REACT_PAGE_TRACKER_INTERNAL__: { pageIndex: 0, referrer: document.referrer, pageHistory: [location.href.replace(location.origin, '')], pageHistoryLength: 1, }, }; if (typeof history.state === 'object' && history.state !== null) { history.replaceState({ ...history.state, ...defaultData, }, location.href); } else { history.replaceState(defaultData, ''); } pageTrackerStore.setState(defaultData.__REACT_PAGE_TRACKER_INTERNAL__); }; export const usePageTrackerHandler = () => { const { initStrictModeDetector } = useStrictModeDetector(); const pageIndex = useRef(0); // for isLastPage usage const visitedTotalLength = useRef(1); useEffect(() => { initHistoryState(); initStrictModeDetector(); /** Handle user operate original back/forward button or history.go()/back()/forward() or framework routing. **/ const handlePopState = (event) => { var _a, _b; const state = event.state; const internal = state === null || state === void 0 ? void 0 : state.__REACT_PAGE_TRACKER_INTERNAL__; if (!internal) { // If the state isn't ours, ignore to avoid corrupting indices return; } const statePageIndex = (_a = internal.pageIndex) !== null && _a !== void 0 ? _a : 0; const pageEvent = pageIndex.current > statePageIndex ? 'back' : 'forward'; if (pageEvent === 'forward') { pageIndex.current = statePageIndex; // moving forward to the state's index } else { pageIndex.current = statePageIndex; } const pageHistory = [...(internal.pageHistory || [])]; pageTrackerStore.setState({ pageIndex: pageIndex.current, isFirstPage: pageIndex.current === 0, isLastPage: pageHistory.length === visitedTotalLength.current, referrer: (_b = internal.referrer) !== null && _b !== void 0 ? _b : '', pageHistory, pageHistoryLength: visitedTotalLength.current, pageEvent, }); }; // save original pushState const originalPushState = history.pushState.bind(history); const normalize = (raw) => raw.replace(location.origin, ''); // override popstate using addEventListener window.addEventListener('popstate', handlePopState); history.pushState = (state, title, url) => { const baseHistory = pageTrackerStore.getImmutablePageHistory(); // trim forward if navigating mid-stack if (pageIndex.current < baseHistory.length - 1) { baseHistory.splice(pageIndex.current + 1); } const nextUrl = url ? normalize(url) : normalize(location.href); const newPageHistory = [...baseHistory, nextUrl]; const newPageIndex = newPageHistory.length - 1; pageIndex.current = newPageIndex; visitedTotalLength.current = newPageHistory.length; const newState = { pageIndex: newPageIndex, referrer: normalize(window.location.href), pageHistory: newPageHistory, pageHistoryLength: visitedTotalLength.current, }; const stateWithPageInfo = { ...state, __REACT_PAGE_TRACKER_INTERNAL__: { ...newState, }, }; pageTrackerStore.setState({ ...newState, isFirstPage: newPageIndex === 0, isLastPage: true, pageEvent: 'push', }); debugLog(`pushState: stateWithPageInfo.pageIndex -->${stateWithPageInfo.__REACT_PAGE_TRACKER_INTERNAL__.pageIndex} ,pageIndex.current --> ${pageIndex.current} referrer -->${stateWithPageInfo.__REACT_PAGE_TRACKER_INTERNAL__.referrer}`); return originalPushState(stateWithPageInfo, title || '', url || ''); }; return () => { window.removeEventListener('popstate', handlePopState); history.pushState = originalPushState; }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); }; export const PageTracker = () => { // eslint-disable-next-line no-use-before-define usePageTrackerHandler(); return null; }; //# sourceMappingURL=page-tracker.js.map