one
Version:
One is a new React Framework that makes Vite serve both native and web.
123 lines (122 loc) • 5.23 kB
JavaScript
import * as React from "react";
import { Platform } from "react-native-web";
let currentLocation = typeof window < "u" ? window.location.pathname + window.location.search : "", isBlocking = !1, isProceeding = !1, pendingNavigation = null;
const blockerCallbacks = /* @__PURE__ */ new Map();
let listenersSetup = !1;
function setupListeners() {
if (listenersSetup || typeof window > "u") return;
listenersSetup = !0, currentLocation = window.location.pathname + window.location.search, window.addEventListener("popstate", () => {
if (isProceeding) return;
const nextLocation = window.location.pathname + window.location.search;
for (const [, callbacks] of blockerCallbacks)
if (callbacks.shouldBlock()) {
isBlocking = !0, pendingNavigation = {
previousLocation: currentLocation,
nextLocation,
historyAction: "pop"
}, window.history.go(1), callbacks.onBlock(pendingNavigation);
return;
}
currentLocation = nextLocation;
});
const originalPushState = window.history.pushState.bind(window.history), originalReplaceState = window.history.replaceState.bind(window.history);
window.history.pushState = function(state, title, url) {
if (isProceeding || !url)
return originalPushState(state, title, url);
const nextLocation = typeof url == "string" ? url : url.toString();
for (const [, callbacks] of blockerCallbacks)
if (callbacks.shouldBlock()) {
isBlocking = !0, pendingNavigation = {
previousLocation: currentLocation,
nextLocation,
historyAction: "push"
}, callbacks.onBlock(pendingNavigation);
return;
}
return currentLocation = nextLocation, originalPushState(state, title, url);
}, window.history.replaceState = function(state, title, url) {
if (isProceeding || !url)
return originalReplaceState(state, title, url);
const nextLocation = typeof url == "string" ? url : url.toString();
for (const [, callbacks] of blockerCallbacks)
if (callbacks.shouldBlock()) {
isBlocking = !0, pendingNavigation = {
previousLocation: currentLocation,
nextLocation,
historyAction: "replace"
}, callbacks.onBlock(pendingNavigation);
return;
}
return currentLocation = nextLocation, originalReplaceState(state, title, url);
}, window.addEventListener("beforeunload", (event) => {
for (const [, callbacks] of blockerCallbacks)
if (callbacks.shouldBlock()) {
event.preventDefault(), event.returnValue = "";
return;
}
});
}
function useBlocker(shouldBlock) {
const [state, setState] = React.useState("unblocked"), [blockedLocation, setBlockedLocation] = React.useState(null), idRef = React.useRef(null), shouldBlockRef = React.useRef(shouldBlock);
shouldBlockRef.current = shouldBlock, React.useEffect(() => {
if (Platform.OS !== "web" || typeof window > "u") return;
setupListeners();
const id = Symbol("blocker");
return idRef.current = id, blockerCallbacks.set(id, {
shouldBlock: () => {
const block = shouldBlockRef.current;
return typeof block == "function" ? block({
currentLocation,
nextLocation: pendingNavigation?.nextLocation || "",
historyAction: pendingNavigation?.historyAction || "push"
}) : block;
},
onBlock: (pending) => {
setBlockedLocation(pending.nextLocation), setState("blocked");
},
onProceed: () => {
setState("proceeding");
},
onReset: () => {
setState("unblocked"), setBlockedLocation(null);
}
}), () => {
blockerCallbacks.delete(id);
};
}, []);
const reset = React.useCallback(() => {
isBlocking = !1, pendingNavigation = null, setBlockedLocation(null), setState("unblocked");
}, []), proceed = React.useCallback(() => {
if (!pendingNavigation) return;
setState("proceeding"), isProceeding = !0;
const pending = pendingNavigation;
pendingNavigation = null, isBlocking = !1, requestAnimationFrame(() => {
pending.historyAction === "pop" ? window.history.back() : pending.historyAction === "push" ? window.history.pushState(null, "", pending.nextLocation) : window.history.replaceState(null, "", pending.nextLocation), currentLocation = pending.nextLocation, requestAnimationFrame(() => {
isProceeding = !1, setBlockedLocation(null), setState("unblocked");
});
});
}, []);
return state === "unblocked" ? { state: "unblocked" } : state === "proceeding" ? { state: "proceeding", location: blockedLocation } : {
state: "blocked",
reset,
proceed,
location: blockedLocation
};
}
function checkBlocker(nextLocation, historyAction = "push") {
if (Platform.OS !== "web" || typeof window > "u" || isProceeding)
return !1;
for (const [, callbacks] of blockerCallbacks)
if (callbacks.shouldBlock())
return isBlocking = !0, pendingNavigation = {
previousLocation: currentLocation,
nextLocation,
historyAction
}, callbacks.onBlock(pendingNavigation), !0;
return !1;
}
export {
checkBlocker,
useBlocker
};
//# sourceMappingURL=useBlocker.js.map