index.js 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037
  1. /**
  2. * React Router DOM v6.8.1
  3. *
  4. * Copyright (c) Remix Software Inc.
  5. *
  6. * This source code is licensed under the MIT license found in the
  7. * LICENSE.md file in the root directory of this source tree.
  8. *
  9. * @license MIT
  10. */
  11. import * as React from 'react';
  12. import { UNSAFE_enhanceManualRouteObjects, Router, useHref, useResolvedPath, useLocation, UNSAFE_DataRouterStateContext, UNSAFE_NavigationContext, useNavigate, createPath, UNSAFE_RouteContext, useMatches, useNavigation, unstable_useBlocker, UNSAFE_DataRouterContext } from 'react-router';
  13. export { AbortedDeferredError, Await, MemoryRouter, Navigate, NavigationType, Outlet, Route, Router, RouterProvider, Routes, UNSAFE_DataRouterContext, UNSAFE_DataRouterStateContext, UNSAFE_LocationContext, UNSAFE_NavigationContext, UNSAFE_RouteContext, UNSAFE_enhanceManualRouteObjects, createMemoryRouter, createPath, createRoutesFromChildren, createRoutesFromElements, defer, generatePath, isRouteErrorResponse, json, matchPath, matchRoutes, parsePath, redirect, renderMatches, resolvePath, unstable_useBlocker, useActionData, useAsyncError, useAsyncValue, useHref, useInRouterContext, useLoaderData, useLocation, useMatch, useMatches, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useRoutes } from 'react-router';
  14. import { createRouter, createBrowserHistory, createHashHistory, ErrorResponse, invariant, joinPaths } from '@remix-run/router';
  15. function _extends() {
  16. _extends = Object.assign ? Object.assign.bind() : function (target) {
  17. for (var i = 1; i < arguments.length; i++) {
  18. var source = arguments[i];
  19. for (var key in source) {
  20. if (Object.prototype.hasOwnProperty.call(source, key)) {
  21. target[key] = source[key];
  22. }
  23. }
  24. }
  25. return target;
  26. };
  27. return _extends.apply(this, arguments);
  28. }
  29. function _objectWithoutPropertiesLoose(source, excluded) {
  30. if (source == null) return {};
  31. var target = {};
  32. var sourceKeys = Object.keys(source);
  33. var key, i;
  34. for (i = 0; i < sourceKeys.length; i++) {
  35. key = sourceKeys[i];
  36. if (excluded.indexOf(key) >= 0) continue;
  37. target[key] = source[key];
  38. }
  39. return target;
  40. }
  41. const defaultMethod = "get";
  42. const defaultEncType = "application/x-www-form-urlencoded";
  43. function isHtmlElement(object) {
  44. return object != null && typeof object.tagName === "string";
  45. }
  46. function isButtonElement(object) {
  47. return isHtmlElement(object) && object.tagName.toLowerCase() === "button";
  48. }
  49. function isFormElement(object) {
  50. return isHtmlElement(object) && object.tagName.toLowerCase() === "form";
  51. }
  52. function isInputElement(object) {
  53. return isHtmlElement(object) && object.tagName.toLowerCase() === "input";
  54. }
  55. function isModifiedEvent(event) {
  56. return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
  57. }
  58. function shouldProcessLinkClick(event, target) {
  59. return event.button === 0 && ( // Ignore everything but left clicks
  60. !target || target === "_self") && // Let browser handle "target=_blank" etc.
  61. !isModifiedEvent(event) // Ignore clicks with modifier keys
  62. ;
  63. }
  64. /**
  65. * Creates a URLSearchParams object using the given initializer.
  66. *
  67. * This is identical to `new URLSearchParams(init)` except it also
  68. * supports arrays as values in the object form of the initializer
  69. * instead of just strings. This is convenient when you need multiple
  70. * values for a given key, but don't want to use an array initializer.
  71. *
  72. * For example, instead of:
  73. *
  74. * let searchParams = new URLSearchParams([
  75. * ['sort', 'name'],
  76. * ['sort', 'price']
  77. * ]);
  78. *
  79. * you can do:
  80. *
  81. * let searchParams = createSearchParams({
  82. * sort: ['name', 'price']
  83. * });
  84. */
  85. function createSearchParams(init) {
  86. if (init === void 0) {
  87. init = "";
  88. }
  89. return new URLSearchParams(typeof init === "string" || Array.isArray(init) || init instanceof URLSearchParams ? init : Object.keys(init).reduce((memo, key) => {
  90. let value = init[key];
  91. return memo.concat(Array.isArray(value) ? value.map(v => [key, v]) : [[key, value]]);
  92. }, []));
  93. }
  94. function getSearchParamsForLocation(locationSearch, defaultSearchParams) {
  95. let searchParams = createSearchParams(locationSearch);
  96. if (defaultSearchParams) {
  97. for (let key of defaultSearchParams.keys()) {
  98. if (!searchParams.has(key)) {
  99. defaultSearchParams.getAll(key).forEach(value => {
  100. searchParams.append(key, value);
  101. });
  102. }
  103. }
  104. }
  105. return searchParams;
  106. }
  107. function getFormSubmissionInfo(target, defaultAction, options) {
  108. let method;
  109. let action;
  110. let encType;
  111. let formData;
  112. if (isFormElement(target)) {
  113. let submissionTrigger = options.submissionTrigger;
  114. method = options.method || target.getAttribute("method") || defaultMethod;
  115. action = options.action || target.getAttribute("action") || defaultAction;
  116. encType = options.encType || target.getAttribute("enctype") || defaultEncType;
  117. formData = new FormData(target);
  118. if (submissionTrigger && submissionTrigger.name) {
  119. formData.append(submissionTrigger.name, submissionTrigger.value);
  120. }
  121. } else if (isButtonElement(target) || isInputElement(target) && (target.type === "submit" || target.type === "image")) {
  122. let form = target.form;
  123. if (form == null) {
  124. throw new Error("Cannot submit a <button> or <input type=\"submit\"> without a <form>");
  125. } // <button>/<input type="submit"> may override attributes of <form>
  126. method = options.method || target.getAttribute("formmethod") || form.getAttribute("method") || defaultMethod;
  127. action = options.action || target.getAttribute("formaction") || form.getAttribute("action") || defaultAction;
  128. encType = options.encType || target.getAttribute("formenctype") || form.getAttribute("enctype") || defaultEncType;
  129. formData = new FormData(form); // Include name + value from a <button>, appending in case the button name
  130. // matches an existing input name
  131. if (target.name) {
  132. formData.append(target.name, target.value);
  133. }
  134. } else if (isHtmlElement(target)) {
  135. throw new Error("Cannot submit element that is not <form>, <button>, or " + "<input type=\"submit|image\">");
  136. } else {
  137. method = options.method || defaultMethod;
  138. action = options.action || defaultAction;
  139. encType = options.encType || defaultEncType;
  140. if (target instanceof FormData) {
  141. formData = target;
  142. } else {
  143. formData = new FormData();
  144. if (target instanceof URLSearchParams) {
  145. for (let [name, value] of target) {
  146. formData.append(name, value);
  147. }
  148. } else if (target != null) {
  149. for (let name of Object.keys(target)) {
  150. formData.append(name, target[name]);
  151. }
  152. }
  153. }
  154. }
  155. let {
  156. protocol,
  157. host
  158. } = window.location;
  159. let url = new URL(action, protocol + "//" + host);
  160. return {
  161. url,
  162. method: method.toLowerCase(),
  163. encType,
  164. formData
  165. };
  166. }
  167. const _excluded = ["onClick", "relative", "reloadDocument", "replace", "state", "target", "to", "preventScrollReset"],
  168. _excluded2 = ["aria-current", "caseSensitive", "className", "end", "style", "to", "children"],
  169. _excluded3 = ["reloadDocument", "replace", "method", "action", "onSubmit", "fetcherKey", "routeId", "relative", "preventScrollReset"];
  170. //#region Routers
  171. ////////////////////////////////////////////////////////////////////////////////
  172. function createBrowserRouter(routes, opts) {
  173. return createRouter({
  174. basename: opts == null ? void 0 : opts.basename,
  175. history: createBrowserHistory({
  176. window: opts == null ? void 0 : opts.window
  177. }),
  178. hydrationData: (opts == null ? void 0 : opts.hydrationData) || parseHydrationData(),
  179. routes: UNSAFE_enhanceManualRouteObjects(routes)
  180. }).initialize();
  181. }
  182. function createHashRouter(routes, opts) {
  183. return createRouter({
  184. basename: opts == null ? void 0 : opts.basename,
  185. history: createHashHistory({
  186. window: opts == null ? void 0 : opts.window
  187. }),
  188. hydrationData: (opts == null ? void 0 : opts.hydrationData) || parseHydrationData(),
  189. routes: UNSAFE_enhanceManualRouteObjects(routes)
  190. }).initialize();
  191. }
  192. function parseHydrationData() {
  193. var _window;
  194. let state = (_window = window) == null ? void 0 : _window.__staticRouterHydrationData;
  195. if (state && state.errors) {
  196. state = _extends({}, state, {
  197. errors: deserializeErrors(state.errors)
  198. });
  199. }
  200. return state;
  201. }
  202. function deserializeErrors(errors) {
  203. if (!errors) return null;
  204. let entries = Object.entries(errors);
  205. let serialized = {};
  206. for (let [key, val] of entries) {
  207. // Hey you! If you change this, please change the corresponding logic in
  208. // serializeErrors in react-router-dom/server.tsx :)
  209. if (val && val.__type === "RouteErrorResponse") {
  210. serialized[key] = new ErrorResponse(val.status, val.statusText, val.data, val.internal === true);
  211. } else if (val && val.__type === "Error") {
  212. let error = new Error(val.message); // Wipe away the client-side stack trace. Nothing to fill it in with
  213. // because we don't serialize SSR stack traces for security reasons
  214. error.stack = "";
  215. serialized[key] = error;
  216. } else {
  217. serialized[key] = val;
  218. }
  219. }
  220. return serialized;
  221. }
  222. /**
  223. * A `<Router>` for use in web browsers. Provides the cleanest URLs.
  224. */
  225. function BrowserRouter(_ref) {
  226. let {
  227. basename,
  228. children,
  229. window
  230. } = _ref;
  231. let historyRef = React.useRef();
  232. if (historyRef.current == null) {
  233. historyRef.current = createBrowserHistory({
  234. window,
  235. v5Compat: true
  236. });
  237. }
  238. let history = historyRef.current;
  239. let [state, setState] = React.useState({
  240. action: history.action,
  241. location: history.location
  242. });
  243. React.useLayoutEffect(() => history.listen(setState), [history]);
  244. return /*#__PURE__*/React.createElement(Router, {
  245. basename: basename,
  246. children: children,
  247. location: state.location,
  248. navigationType: state.action,
  249. navigator: history
  250. });
  251. }
  252. /**
  253. * A `<Router>` for use in web browsers. Stores the location in the hash
  254. * portion of the URL so it is not sent to the server.
  255. */
  256. function HashRouter(_ref2) {
  257. let {
  258. basename,
  259. children,
  260. window
  261. } = _ref2;
  262. let historyRef = React.useRef();
  263. if (historyRef.current == null) {
  264. historyRef.current = createHashHistory({
  265. window,
  266. v5Compat: true
  267. });
  268. }
  269. let history = historyRef.current;
  270. let [state, setState] = React.useState({
  271. action: history.action,
  272. location: history.location
  273. });
  274. React.useLayoutEffect(() => history.listen(setState), [history]);
  275. return /*#__PURE__*/React.createElement(Router, {
  276. basename: basename,
  277. children: children,
  278. location: state.location,
  279. navigationType: state.action,
  280. navigator: history
  281. });
  282. }
  283. /**
  284. * A `<Router>` that accepts a pre-instantiated history object. It's important
  285. * to note that using your own history object is highly discouraged and may add
  286. * two versions of the history library to your bundles unless you use the same
  287. * version of the history library that React Router uses internally.
  288. */
  289. function HistoryRouter(_ref3) {
  290. let {
  291. basename,
  292. children,
  293. history
  294. } = _ref3;
  295. const [state, setState] = React.useState({
  296. action: history.action,
  297. location: history.location
  298. });
  299. React.useLayoutEffect(() => history.listen(setState), [history]);
  300. return /*#__PURE__*/React.createElement(Router, {
  301. basename: basename,
  302. children: children,
  303. location: state.location,
  304. navigationType: state.action,
  305. navigator: history
  306. });
  307. }
  308. if (process.env.NODE_ENV !== "production") {
  309. HistoryRouter.displayName = "unstable_HistoryRouter";
  310. }
  311. const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
  312. /**
  313. * The public API for rendering a history-aware <a>.
  314. */
  315. const Link = /*#__PURE__*/React.forwardRef(function LinkWithRef(_ref4, ref) {
  316. let {
  317. onClick,
  318. relative,
  319. reloadDocument,
  320. replace,
  321. state,
  322. target,
  323. to,
  324. preventScrollReset
  325. } = _ref4,
  326. rest = _objectWithoutPropertiesLoose(_ref4, _excluded);
  327. // Rendered into <a href> for absolute URLs
  328. let absoluteHref;
  329. let isExternal = false;
  330. if (isBrowser && typeof to === "string" && /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(to)) {
  331. absoluteHref = to;
  332. let currentUrl = new URL(window.location.href);
  333. let targetUrl = to.startsWith("//") ? new URL(currentUrl.protocol + to) : new URL(to);
  334. if (targetUrl.origin === currentUrl.origin) {
  335. // Strip the protocol/origin for same-origin absolute URLs
  336. to = targetUrl.pathname + targetUrl.search + targetUrl.hash;
  337. } else {
  338. isExternal = true;
  339. }
  340. } // Rendered into <a href> for relative URLs
  341. let href = useHref(to, {
  342. relative
  343. });
  344. let internalOnClick = useLinkClickHandler(to, {
  345. replace,
  346. state,
  347. target,
  348. preventScrollReset,
  349. relative
  350. });
  351. function handleClick(event) {
  352. if (onClick) onClick(event);
  353. if (!event.defaultPrevented) {
  354. internalOnClick(event);
  355. }
  356. }
  357. return (
  358. /*#__PURE__*/
  359. // eslint-disable-next-line jsx-a11y/anchor-has-content
  360. React.createElement("a", _extends({}, rest, {
  361. href: absoluteHref || href,
  362. onClick: isExternal || reloadDocument ? onClick : handleClick,
  363. ref: ref,
  364. target: target
  365. }))
  366. );
  367. });
  368. if (process.env.NODE_ENV !== "production") {
  369. Link.displayName = "Link";
  370. }
  371. /**
  372. * A <Link> wrapper that knows if it's "active" or not.
  373. */
  374. const NavLink = /*#__PURE__*/React.forwardRef(function NavLinkWithRef(_ref5, ref) {
  375. let {
  376. "aria-current": ariaCurrentProp = "page",
  377. caseSensitive = false,
  378. className: classNameProp = "",
  379. end = false,
  380. style: styleProp,
  381. to,
  382. children
  383. } = _ref5,
  384. rest = _objectWithoutPropertiesLoose(_ref5, _excluded2);
  385. let path = useResolvedPath(to, {
  386. relative: rest.relative
  387. });
  388. let location = useLocation();
  389. let routerState = React.useContext(UNSAFE_DataRouterStateContext);
  390. let {
  391. navigator
  392. } = React.useContext(UNSAFE_NavigationContext);
  393. let toPathname = navigator.encodeLocation ? navigator.encodeLocation(path).pathname : path.pathname;
  394. let locationPathname = location.pathname;
  395. let nextLocationPathname = routerState && routerState.navigation && routerState.navigation.location ? routerState.navigation.location.pathname : null;
  396. if (!caseSensitive) {
  397. locationPathname = locationPathname.toLowerCase();
  398. nextLocationPathname = nextLocationPathname ? nextLocationPathname.toLowerCase() : null;
  399. toPathname = toPathname.toLowerCase();
  400. }
  401. let isActive = locationPathname === toPathname || !end && locationPathname.startsWith(toPathname) && locationPathname.charAt(toPathname.length) === "/";
  402. let isPending = nextLocationPathname != null && (nextLocationPathname === toPathname || !end && nextLocationPathname.startsWith(toPathname) && nextLocationPathname.charAt(toPathname.length) === "/");
  403. let ariaCurrent = isActive ? ariaCurrentProp : undefined;
  404. let className;
  405. if (typeof classNameProp === "function") {
  406. className = classNameProp({
  407. isActive,
  408. isPending
  409. });
  410. } else {
  411. // If the className prop is not a function, we use a default `active`
  412. // class for <NavLink />s that are active. In v5 `active` was the default
  413. // value for `activeClassName`, but we are removing that API and can still
  414. // use the old default behavior for a cleaner upgrade path and keep the
  415. // simple styling rules working as they currently do.
  416. className = [classNameProp, isActive ? "active" : null, isPending ? "pending" : null].filter(Boolean).join(" ");
  417. }
  418. let style = typeof styleProp === "function" ? styleProp({
  419. isActive,
  420. isPending
  421. }) : styleProp;
  422. return /*#__PURE__*/React.createElement(Link, _extends({}, rest, {
  423. "aria-current": ariaCurrent,
  424. className: className,
  425. ref: ref,
  426. style: style,
  427. to: to
  428. }), typeof children === "function" ? children({
  429. isActive,
  430. isPending
  431. }) : children);
  432. });
  433. if (process.env.NODE_ENV !== "production") {
  434. NavLink.displayName = "NavLink";
  435. }
  436. /**
  437. * A `@remix-run/router`-aware `<form>`. It behaves like a normal form except
  438. * that the interaction with the server is with `fetch` instead of new document
  439. * requests, allowing components to add nicer UX to the page as the form is
  440. * submitted and returns with data.
  441. */
  442. const Form = /*#__PURE__*/React.forwardRef((props, ref) => {
  443. return /*#__PURE__*/React.createElement(FormImpl, _extends({}, props, {
  444. ref: ref
  445. }));
  446. });
  447. if (process.env.NODE_ENV !== "production") {
  448. Form.displayName = "Form";
  449. }
  450. const FormImpl = /*#__PURE__*/React.forwardRef((_ref6, forwardedRef) => {
  451. let {
  452. reloadDocument,
  453. replace,
  454. method = defaultMethod,
  455. action,
  456. onSubmit,
  457. fetcherKey,
  458. routeId,
  459. relative,
  460. preventScrollReset
  461. } = _ref6,
  462. props = _objectWithoutPropertiesLoose(_ref6, _excluded3);
  463. let submit = useSubmitImpl(fetcherKey, routeId);
  464. let formMethod = method.toLowerCase() === "get" ? "get" : "post";
  465. let formAction = useFormAction(action, {
  466. relative
  467. });
  468. let submitHandler = event => {
  469. onSubmit && onSubmit(event);
  470. if (event.defaultPrevented) return;
  471. event.preventDefault();
  472. let submitter = event.nativeEvent.submitter;
  473. let submitMethod = (submitter == null ? void 0 : submitter.getAttribute("formmethod")) || method;
  474. submit(submitter || event.currentTarget, {
  475. method: submitMethod,
  476. replace,
  477. relative,
  478. preventScrollReset
  479. });
  480. };
  481. return /*#__PURE__*/React.createElement("form", _extends({
  482. ref: forwardedRef,
  483. method: formMethod,
  484. action: formAction,
  485. onSubmit: reloadDocument ? onSubmit : submitHandler
  486. }, props));
  487. });
  488. if (process.env.NODE_ENV !== "production") {
  489. FormImpl.displayName = "FormImpl";
  490. }
  491. /**
  492. * This component will emulate the browser's scroll restoration on location
  493. * changes.
  494. */
  495. function ScrollRestoration(_ref7) {
  496. let {
  497. getKey,
  498. storageKey
  499. } = _ref7;
  500. useScrollRestoration({
  501. getKey,
  502. storageKey
  503. });
  504. return null;
  505. }
  506. if (process.env.NODE_ENV !== "production") {
  507. ScrollRestoration.displayName = "ScrollRestoration";
  508. } //#endregion
  509. ////////////////////////////////////////////////////////////////////////////////
  510. //#region Hooks
  511. ////////////////////////////////////////////////////////////////////////////////
  512. var DataRouterHook;
  513. (function (DataRouterHook) {
  514. DataRouterHook["UseScrollRestoration"] = "useScrollRestoration";
  515. DataRouterHook["UseSubmitImpl"] = "useSubmitImpl";
  516. DataRouterHook["UseFetcher"] = "useFetcher";
  517. })(DataRouterHook || (DataRouterHook = {}));
  518. var DataRouterStateHook;
  519. (function (DataRouterStateHook) {
  520. DataRouterStateHook["UseFetchers"] = "useFetchers";
  521. DataRouterStateHook["UseScrollRestoration"] = "useScrollRestoration";
  522. })(DataRouterStateHook || (DataRouterStateHook = {}));
  523. function getDataRouterConsoleError(hookName) {
  524. return hookName + " must be used within a data router. See https://reactrouter.com/routers/picking-a-router.";
  525. }
  526. function useDataRouterContext(hookName) {
  527. let ctx = React.useContext(UNSAFE_DataRouterContext);
  528. !ctx ? process.env.NODE_ENV !== "production" ? invariant(false, getDataRouterConsoleError(hookName)) : invariant(false) : void 0;
  529. return ctx;
  530. }
  531. function useDataRouterState(hookName) {
  532. let state = React.useContext(UNSAFE_DataRouterStateContext);
  533. !state ? process.env.NODE_ENV !== "production" ? invariant(false, getDataRouterConsoleError(hookName)) : invariant(false) : void 0;
  534. return state;
  535. }
  536. /**
  537. * Handles the click behavior for router `<Link>` components. This is useful if
  538. * you need to create custom `<Link>` components with the same click behavior we
  539. * use in our exported `<Link>`.
  540. */
  541. function useLinkClickHandler(to, _temp) {
  542. let {
  543. target,
  544. replace: replaceProp,
  545. state,
  546. preventScrollReset,
  547. relative
  548. } = _temp === void 0 ? {} : _temp;
  549. let navigate = useNavigate();
  550. let location = useLocation();
  551. let path = useResolvedPath(to, {
  552. relative
  553. });
  554. return React.useCallback(event => {
  555. if (shouldProcessLinkClick(event, target)) {
  556. event.preventDefault(); // If the URL hasn't changed, a regular <a> will do a replace instead of
  557. // a push, so do the same here unless the replace prop is explicitly set
  558. let replace = replaceProp !== undefined ? replaceProp : createPath(location) === createPath(path);
  559. navigate(to, {
  560. replace,
  561. state,
  562. preventScrollReset,
  563. relative
  564. });
  565. }
  566. }, [location, navigate, path, replaceProp, state, target, to, preventScrollReset, relative]);
  567. }
  568. /**
  569. * A convenient wrapper for reading and writing search parameters via the
  570. * URLSearchParams interface.
  571. */
  572. function useSearchParams(defaultInit) {
  573. process.env.NODE_ENV !== "production" ? warning(typeof URLSearchParams !== "undefined", "You cannot use the `useSearchParams` hook in a browser that does not " + "support the URLSearchParams API. If you need to support Internet " + "Explorer 11, we recommend you load a polyfill such as " + "https://github.com/ungap/url-search-params\n\n" + "If you're unsure how to load polyfills, we recommend you check out " + "https://polyfill.io/v3/ which provides some recommendations about how " + "to load polyfills only for users that need them, instead of for every " + "user.") : void 0;
  574. let defaultSearchParamsRef = React.useRef(createSearchParams(defaultInit));
  575. let hasSetSearchParamsRef = React.useRef(false);
  576. let location = useLocation();
  577. let searchParams = React.useMemo(() => // Only merge in the defaults if we haven't yet called setSearchParams.
  578. // Once we call that we want those to take precedence, otherwise you can't
  579. // remove a param with setSearchParams({}) if it has an initial value
  580. getSearchParamsForLocation(location.search, hasSetSearchParamsRef.current ? null : defaultSearchParamsRef.current), [location.search]);
  581. let navigate = useNavigate();
  582. let setSearchParams = React.useCallback((nextInit, navigateOptions) => {
  583. const newSearchParams = createSearchParams(typeof nextInit === "function" ? nextInit(searchParams) : nextInit);
  584. hasSetSearchParamsRef.current = true;
  585. navigate("?" + newSearchParams, navigateOptions);
  586. }, [navigate, searchParams]);
  587. return [searchParams, setSearchParams];
  588. }
  589. /**
  590. * Returns a function that may be used to programmatically submit a form (or
  591. * some arbitrary data) to the server.
  592. */
  593. function useSubmit() {
  594. return useSubmitImpl();
  595. }
  596. function useSubmitImpl(fetcherKey, routeId) {
  597. let {
  598. router
  599. } = useDataRouterContext(DataRouterHook.UseSubmitImpl);
  600. let defaultAction = useFormAction();
  601. return React.useCallback(function (target, options) {
  602. if (options === void 0) {
  603. options = {};
  604. }
  605. if (typeof document === "undefined") {
  606. throw new Error("You are calling submit during the server render. " + "Try calling submit within a `useEffect` or callback instead.");
  607. }
  608. let {
  609. method,
  610. encType,
  611. formData,
  612. url
  613. } = getFormSubmissionInfo(target, defaultAction, options);
  614. let href = url.pathname + url.search;
  615. let opts = {
  616. replace: options.replace,
  617. preventScrollReset: options.preventScrollReset,
  618. formData,
  619. formMethod: method,
  620. formEncType: encType
  621. };
  622. if (fetcherKey) {
  623. !(routeId != null) ? process.env.NODE_ENV !== "production" ? invariant(false, "No routeId available for useFetcher()") : invariant(false) : void 0;
  624. router.fetch(fetcherKey, routeId, href, opts);
  625. } else {
  626. router.navigate(href, opts);
  627. }
  628. }, [defaultAction, router, fetcherKey, routeId]);
  629. }
  630. function useFormAction(action, _temp2) {
  631. let {
  632. relative
  633. } = _temp2 === void 0 ? {} : _temp2;
  634. let {
  635. basename
  636. } = React.useContext(UNSAFE_NavigationContext);
  637. let routeContext = React.useContext(UNSAFE_RouteContext);
  638. !routeContext ? process.env.NODE_ENV !== "production" ? invariant(false, "useFormAction must be used inside a RouteContext") : invariant(false) : void 0;
  639. let [match] = routeContext.matches.slice(-1); // Shallow clone path so we can modify it below, otherwise we modify the
  640. // object referenced by useMemo inside useResolvedPath
  641. let path = _extends({}, useResolvedPath(action ? action : ".", {
  642. relative
  643. })); // Previously we set the default action to ".". The problem with this is that
  644. // `useResolvedPath(".")` excludes search params and the hash of the resolved
  645. // URL. This is the intended behavior of when "." is specifically provided as
  646. // the form action, but inconsistent w/ browsers when the action is omitted.
  647. // https://github.com/remix-run/remix/issues/927
  648. let location = useLocation();
  649. if (action == null) {
  650. // Safe to write to these directly here since if action was undefined, we
  651. // would have called useResolvedPath(".") which will never include a search
  652. // or hash
  653. path.search = location.search;
  654. path.hash = location.hash; // When grabbing search params from the URL, remove the automatically
  655. // inserted ?index param so we match the useResolvedPath search behavior
  656. // which would not include ?index
  657. if (match.route.index) {
  658. let params = new URLSearchParams(path.search);
  659. params.delete("index");
  660. path.search = params.toString() ? "?" + params.toString() : "";
  661. }
  662. }
  663. if ((!action || action === ".") && match.route.index) {
  664. path.search = path.search ? path.search.replace(/^\?/, "?index&") : "?index";
  665. } // If we're operating within a basename, prepend it to the pathname prior
  666. // to creating the form action. If this is a root navigation, then just use
  667. // the raw basename which allows the basename to have full control over the
  668. // presence of a trailing slash on root actions
  669. if (basename !== "/") {
  670. path.pathname = path.pathname === "/" ? basename : joinPaths([basename, path.pathname]);
  671. }
  672. return createPath(path);
  673. }
  674. function createFetcherForm(fetcherKey, routeId) {
  675. let FetcherForm = /*#__PURE__*/React.forwardRef((props, ref) => {
  676. return /*#__PURE__*/React.createElement(FormImpl, _extends({}, props, {
  677. ref: ref,
  678. fetcherKey: fetcherKey,
  679. routeId: routeId
  680. }));
  681. });
  682. if (process.env.NODE_ENV !== "production") {
  683. FetcherForm.displayName = "fetcher.Form";
  684. }
  685. return FetcherForm;
  686. }
  687. let fetcherId = 0;
  688. /**
  689. * Interacts with route loaders and actions without causing a navigation. Great
  690. * for any interaction that stays on the same page.
  691. */
  692. function useFetcher() {
  693. var _route$matches;
  694. let {
  695. router
  696. } = useDataRouterContext(DataRouterHook.UseFetcher);
  697. let route = React.useContext(UNSAFE_RouteContext);
  698. !route ? process.env.NODE_ENV !== "production" ? invariant(false, "useFetcher must be used inside a RouteContext") : invariant(false) : void 0;
  699. let routeId = (_route$matches = route.matches[route.matches.length - 1]) == null ? void 0 : _route$matches.route.id;
  700. !(routeId != null) ? process.env.NODE_ENV !== "production" ? invariant(false, "useFetcher can only be used on routes that contain a unique \"id\"") : invariant(false) : void 0;
  701. let [fetcherKey] = React.useState(() => String(++fetcherId));
  702. let [Form] = React.useState(() => {
  703. !routeId ? process.env.NODE_ENV !== "production" ? invariant(false, "No routeId available for fetcher.Form()") : invariant(false) : void 0;
  704. return createFetcherForm(fetcherKey, routeId);
  705. });
  706. let [load] = React.useState(() => href => {
  707. !router ? process.env.NODE_ENV !== "production" ? invariant(false, "No router available for fetcher.load()") : invariant(false) : void 0;
  708. !routeId ? process.env.NODE_ENV !== "production" ? invariant(false, "No routeId available for fetcher.load()") : invariant(false) : void 0;
  709. router.fetch(fetcherKey, routeId, href);
  710. });
  711. let submit = useSubmitImpl(fetcherKey, routeId);
  712. let fetcher = router.getFetcher(fetcherKey);
  713. let fetcherWithComponents = React.useMemo(() => _extends({
  714. Form,
  715. submit,
  716. load
  717. }, fetcher), [fetcher, Form, submit, load]);
  718. React.useEffect(() => {
  719. // Is this busted when the React team gets real weird and calls effects
  720. // twice on mount? We really just need to garbage collect here when this
  721. // fetcher is no longer around.
  722. return () => {
  723. if (!router) {
  724. console.warn("No fetcher available to clean up from useFetcher()");
  725. return;
  726. }
  727. router.deleteFetcher(fetcherKey);
  728. };
  729. }, [router, fetcherKey]);
  730. return fetcherWithComponents;
  731. }
  732. /**
  733. * Provides all fetchers currently on the page. Useful for layouts and parent
  734. * routes that need to provide pending/optimistic UI regarding the fetch.
  735. */
  736. function useFetchers() {
  737. let state = useDataRouterState(DataRouterStateHook.UseFetchers);
  738. return [...state.fetchers.values()];
  739. }
  740. const SCROLL_RESTORATION_STORAGE_KEY = "react-router-scroll-positions";
  741. let savedScrollPositions = {};
  742. /**
  743. * When rendered inside a RouterProvider, will restore scroll positions on navigations
  744. */
  745. function useScrollRestoration(_temp3) {
  746. let {
  747. getKey,
  748. storageKey
  749. } = _temp3 === void 0 ? {} : _temp3;
  750. let {
  751. router
  752. } = useDataRouterContext(DataRouterHook.UseScrollRestoration);
  753. let {
  754. restoreScrollPosition,
  755. preventScrollReset
  756. } = useDataRouterState(DataRouterStateHook.UseScrollRestoration);
  757. let location = useLocation();
  758. let matches = useMatches();
  759. let navigation = useNavigation(); // Trigger manual scroll restoration while we're active
  760. React.useEffect(() => {
  761. window.history.scrollRestoration = "manual";
  762. return () => {
  763. window.history.scrollRestoration = "auto";
  764. };
  765. }, []); // Save positions on pagehide
  766. usePageHide(React.useCallback(() => {
  767. if (navigation.state === "idle") {
  768. let key = (getKey ? getKey(location, matches) : null) || location.key;
  769. savedScrollPositions[key] = window.scrollY;
  770. }
  771. sessionStorage.setItem(storageKey || SCROLL_RESTORATION_STORAGE_KEY, JSON.stringify(savedScrollPositions));
  772. window.history.scrollRestoration = "auto";
  773. }, [storageKey, getKey, navigation.state, location, matches])); // Read in any saved scroll locations
  774. if (typeof document !== "undefined") {
  775. // eslint-disable-next-line react-hooks/rules-of-hooks
  776. React.useLayoutEffect(() => {
  777. try {
  778. let sessionPositions = sessionStorage.getItem(storageKey || SCROLL_RESTORATION_STORAGE_KEY);
  779. if (sessionPositions) {
  780. savedScrollPositions = JSON.parse(sessionPositions);
  781. }
  782. } catch (e) {// no-op, use default empty object
  783. }
  784. }, [storageKey]); // Enable scroll restoration in the router
  785. // eslint-disable-next-line react-hooks/rules-of-hooks
  786. React.useLayoutEffect(() => {
  787. let disableScrollRestoration = router == null ? void 0 : router.enableScrollRestoration(savedScrollPositions, () => window.scrollY, getKey);
  788. return () => disableScrollRestoration && disableScrollRestoration();
  789. }, [router, getKey]); // Restore scrolling when state.restoreScrollPosition changes
  790. // eslint-disable-next-line react-hooks/rules-of-hooks
  791. React.useLayoutEffect(() => {
  792. // Explicit false means don't do anything (used for submissions)
  793. if (restoreScrollPosition === false) {
  794. return;
  795. } // been here before, scroll to it
  796. if (typeof restoreScrollPosition === "number") {
  797. window.scrollTo(0, restoreScrollPosition);
  798. return;
  799. } // try to scroll to the hash
  800. if (location.hash) {
  801. let el = document.getElementById(location.hash.slice(1));
  802. if (el) {
  803. el.scrollIntoView();
  804. return;
  805. }
  806. } // Don't reset if this navigation opted out
  807. if (preventScrollReset === true) {
  808. return;
  809. } // otherwise go to the top on new locations
  810. window.scrollTo(0, 0);
  811. }, [location, restoreScrollPosition, preventScrollReset]);
  812. }
  813. }
  814. /**
  815. * Setup a callback to be fired on the window's `beforeunload` event. This is
  816. * useful for saving some data to `window.localStorage` just before the page
  817. * refreshes.
  818. *
  819. * Note: The `callback` argument should be a function created with
  820. * `React.useCallback()`.
  821. */
  822. function useBeforeUnload(callback, options) {
  823. let {
  824. capture
  825. } = options || {};
  826. React.useEffect(() => {
  827. let opts = capture != null ? {
  828. capture
  829. } : undefined;
  830. window.addEventListener("beforeunload", callback, opts);
  831. return () => {
  832. window.removeEventListener("beforeunload", callback, opts);
  833. };
  834. }, [callback, capture]);
  835. }
  836. /**
  837. * Setup a callback to be fired on the window's `pagehide` event. This is
  838. * useful for saving some data to `window.localStorage` just before the page
  839. * refreshes. This event is better supported than beforeunload across browsers.
  840. *
  841. * Note: The `callback` argument should be a function created with
  842. * `React.useCallback()`.
  843. */
  844. function usePageHide(callback, options) {
  845. let {
  846. capture
  847. } = options || {};
  848. React.useEffect(() => {
  849. let opts = capture != null ? {
  850. capture
  851. } : undefined;
  852. window.addEventListener("pagehide", callback, opts);
  853. return () => {
  854. window.removeEventListener("pagehide", callback, opts);
  855. };
  856. }, [callback, capture]);
  857. }
  858. /**
  859. * Wrapper around useBlocker to show a window.confirm prompt to users instead
  860. * of building a custom UI with useBlocker.
  861. *
  862. * Warning: This has *a lot of rough edges* and behaves very differently (and
  863. * very incorrectly in some cases) across browsers if user click addition
  864. * back/forward navigations while the confirm is open. Use at your own risk.
  865. */
  866. function usePrompt(_ref8) {
  867. let {
  868. when,
  869. message
  870. } = _ref8;
  871. let blocker = unstable_useBlocker(when);
  872. React.useEffect(() => {
  873. if (blocker.state === "blocked" && !when) {
  874. blocker.reset();
  875. }
  876. }, [blocker, when]);
  877. React.useEffect(() => {
  878. if (blocker.state === "blocked") {
  879. let proceed = window.confirm(message);
  880. if (proceed) {
  881. setTimeout(blocker.proceed, 0);
  882. } else {
  883. blocker.reset();
  884. }
  885. }
  886. }, [blocker, message]);
  887. }
  888. ////////////////////////////////////////////////////////////////////////////////
  889. //#region Utils
  890. ////////////////////////////////////////////////////////////////////////////////
  891. function warning(cond, message) {
  892. if (!cond) {
  893. // eslint-disable-next-line no-console
  894. if (typeof console !== "undefined") console.warn(message);
  895. try {
  896. // Welcome to debugging React Router!
  897. //
  898. // This error is thrown as a convenience so you can more easily
  899. // find the source for a warning that appears in the console by
  900. // enabling "pause on exceptions" in your JavaScript debugger.
  901. throw new Error(message); // eslint-disable-next-line no-empty
  902. } catch (e) {}
  903. }
  904. } //#endregion
  905. export { BrowserRouter, Form, HashRouter, Link, NavLink, ScrollRestoration, useScrollRestoration as UNSAFE_useScrollRestoration, createBrowserRouter, createHashRouter, createSearchParams, HistoryRouter as unstable_HistoryRouter, usePrompt as unstable_usePrompt, useBeforeUnload, useFetcher, useFetchers, useFormAction, useLinkClickHandler, useSearchParams, useSubmit };
  906. //# sourceMappingURL=index.js.map