@tanstack/react-router 0.0.1-beta.20 → 0.0.1-beta.200

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,642 @@
1
+ /**
2
+ * @tanstack/react-router/src/index.tsx
3
+ *
4
+ * Copyright (c) TanStack
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
+ 'use strict';
12
+
13
+ Object.defineProperty(exports, '__esModule', { value: true });
14
+
15
+ var _rollupPluginBabelHelpers = require('./_virtual/_rollupPluginBabelHelpers.js');
16
+ var React = require('react');
17
+ var reactStore = require('@tanstack/react-store');
18
+ var invariant = require('tiny-invariant');
19
+ var warning = require('tiny-warning');
20
+ var routerCore = require('@tanstack/router-core');
21
+
22
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
23
+
24
+ function _interopNamespace(e) {
25
+ if (e && e.__esModule) return e;
26
+ var n = Object.create(null);
27
+ if (e) {
28
+ Object.keys(e).forEach(function (k) {
29
+ if (k !== 'default') {
30
+ var d = Object.getOwnPropertyDescriptor(e, k);
31
+ Object.defineProperty(n, k, d.get ? d : {
32
+ enumerable: true,
33
+ get: function () { return e[k]; }
34
+ });
35
+ }
36
+ });
37
+ }
38
+ n["default"] = e;
39
+ return Object.freeze(n);
40
+ }
41
+
42
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
43
+ var invariant__default = /*#__PURE__*/_interopDefaultLegacy(invariant);
44
+ var warning__default = /*#__PURE__*/_interopDefaultLegacy(warning);
45
+
46
+ const useLayoutEffect = typeof window !== 'undefined' ? React__namespace.useLayoutEffect : React__namespace.useEffect;
47
+ routerCore.Route.__onInit = route => {
48
+ Object.assign(route, {
49
+ useMatch: (opts = {}) => {
50
+ return useMatch({
51
+ ...opts,
52
+ from: route.id
53
+ });
54
+ },
55
+ useLoader: (opts = {}) => {
56
+ return useLoader({
57
+ ...opts,
58
+ from: route.id
59
+ });
60
+ },
61
+ useRouteContext: (opts = {}) => {
62
+ return useMatch({
63
+ ...opts,
64
+ from: route.id,
65
+ select: d => opts?.select ? opts.select(d.context) : d.context
66
+ });
67
+ },
68
+ useSearch: (opts = {}) => {
69
+ return useSearch({
70
+ ...opts,
71
+ from: route.id
72
+ });
73
+ },
74
+ useParams: (opts = {}) => {
75
+ return useParams({
76
+ ...opts,
77
+ from: route.id
78
+ });
79
+ }
80
+ });
81
+ };
82
+
83
+ //
84
+
85
+ function lazyRouteComponent(importer, exportName) {
86
+ let loadPromise;
87
+ const load = () => {
88
+ if (!loadPromise) {
89
+ loadPromise = importer();
90
+ }
91
+ return loadPromise;
92
+ };
93
+ const lazyComp = /*#__PURE__*/React__namespace.lazy(async () => {
94
+ const moduleExports = await load();
95
+ const comp = moduleExports[exportName ?? 'default'];
96
+ return {
97
+ default: comp
98
+ };
99
+ });
100
+ lazyComp.preload = load;
101
+ return lazyComp;
102
+ }
103
+ //
104
+
105
+ function useLinkProps(options) {
106
+ const router = useRouter();
107
+ const {
108
+ // custom props
109
+ type,
110
+ children,
111
+ target,
112
+ activeProps = () => ({
113
+ className: 'active'
114
+ }),
115
+ inactiveProps = () => ({}),
116
+ activeOptions,
117
+ disabled,
118
+ // fromCurrent,
119
+ hash,
120
+ search,
121
+ params,
122
+ to = '.',
123
+ state,
124
+ mask,
125
+ preload,
126
+ preloadDelay,
127
+ replace,
128
+ // element props
129
+ style,
130
+ className,
131
+ onClick,
132
+ onFocus,
133
+ onMouseEnter,
134
+ onMouseLeave,
135
+ onTouchStart,
136
+ ...rest
137
+ } = options;
138
+ const linkInfo = router.buildLink(options);
139
+ if (linkInfo.type === 'external') {
140
+ const {
141
+ href
142
+ } = linkInfo;
143
+ return {
144
+ href
145
+ };
146
+ }
147
+ const {
148
+ handleClick,
149
+ handleFocus,
150
+ handleEnter,
151
+ handleLeave,
152
+ handleTouchStart,
153
+ isActive,
154
+ next
155
+ } = linkInfo;
156
+ const handleReactClick = e => {
157
+ if (options.startTransition ?? true) {
158
+ (React__namespace.startTransition || (d => d))(() => {
159
+ handleClick(e);
160
+ });
161
+ }
162
+ };
163
+ const composeHandlers = handlers => e => {
164
+ if (e.persist) e.persist();
165
+ handlers.filter(Boolean).forEach(handler => {
166
+ if (e.defaultPrevented) return;
167
+ handler(e);
168
+ });
169
+ };
170
+
171
+ // Get the active props
172
+ const resolvedActiveProps = isActive ? routerCore.functionalUpdate(activeProps, {}) ?? {} : {};
173
+
174
+ // Get the inactive props
175
+ const resolvedInactiveProps = isActive ? {} : routerCore.functionalUpdate(inactiveProps, {}) ?? {};
176
+ return {
177
+ ...resolvedActiveProps,
178
+ ...resolvedInactiveProps,
179
+ ...rest,
180
+ href: disabled ? undefined : next.maskedLocation ? next.maskedLocation.href : next.href,
181
+ onClick: composeHandlers([onClick, handleReactClick]),
182
+ onFocus: composeHandlers([onFocus, handleFocus]),
183
+ onMouseEnter: composeHandlers([onMouseEnter, handleEnter]),
184
+ onMouseLeave: composeHandlers([onMouseLeave, handleLeave]),
185
+ onTouchStart: composeHandlers([onTouchStart, handleTouchStart]),
186
+ target,
187
+ style: {
188
+ ...style,
189
+ ...resolvedActiveProps.style,
190
+ ...resolvedInactiveProps.style
191
+ },
192
+ className: [className, resolvedActiveProps.className, resolvedInactiveProps.className].filter(Boolean).join(' ') || undefined,
193
+ ...(disabled ? {
194
+ role: 'link',
195
+ 'aria-disabled': true
196
+ } : undefined),
197
+ ['data-status']: isActive ? 'active' : undefined
198
+ };
199
+ }
200
+ const Link = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
201
+ const linkProps = useLinkProps(props);
202
+ return /*#__PURE__*/React__namespace.createElement("a", _rollupPluginBabelHelpers["extends"]({
203
+ ref: ref
204
+ }, linkProps, {
205
+ children: typeof props.children === 'function' ? props.children({
206
+ isActive: linkProps['data-status'] === 'active'
207
+ }) : props.children
208
+ }));
209
+ });
210
+ function Navigate(props) {
211
+ const router = useRouter();
212
+ useLayoutEffect(() => {
213
+ router.navigate(props);
214
+ }, []);
215
+ return null;
216
+ }
217
+ const matchIdsContext = /*#__PURE__*/React__namespace.createContext(null);
218
+ const routerContext = /*#__PURE__*/React__namespace.createContext(null);
219
+ function useRouterState(opts) {
220
+ const router = useRouter();
221
+ return reactStore.useStore(router.__store, opts?.select);
222
+ }
223
+ function RouterProvider({
224
+ router,
225
+ ...rest
226
+ }) {
227
+ router.update(rest);
228
+ React__namespace.useEffect(() => {
229
+ let unsub;
230
+ React__namespace.startTransition(() => {
231
+ unsub = router.mount();
232
+ });
233
+ return unsub;
234
+ }, [router]);
235
+ const Wrap = router.options.Wrap || React__namespace.Fragment;
236
+ return /*#__PURE__*/React__namespace.createElement(Wrap, null, /*#__PURE__*/React__namespace.createElement(routerContext.Provider, {
237
+ value: router
238
+ }, /*#__PURE__*/React__namespace.createElement(Matches, null)));
239
+ }
240
+ function Matches() {
241
+ const router = useRouter();
242
+ const matchIds = useRouterState({
243
+ select: state => {
244
+ return state.renderedMatchIds;
245
+ }
246
+ });
247
+ const locationKey = useRouterState({
248
+ select: d => d.resolvedLocation.state?.key
249
+ });
250
+ const route = router.getRoute(routerCore.rootRouteId);
251
+ const errorComponent = React__namespace.useCallback(props => {
252
+ return /*#__PURE__*/React__namespace.createElement(ErrorComponent, {
253
+ ...props,
254
+ useMatch: route.useMatch,
255
+ useRouteContext: route.useRouteContext,
256
+ useSearch: route.useSearch,
257
+ useParams: route.useParams
258
+ });
259
+ }, [route]);
260
+ return /*#__PURE__*/React__namespace.createElement(matchIdsContext.Provider, {
261
+ value: [undefined, ...matchIds]
262
+ }, /*#__PURE__*/React__namespace.createElement(CatchBoundary, {
263
+ resetKey: locationKey,
264
+ errorComponent: errorComponent,
265
+ onCatch: () => {
266
+ warning__default["default"](false, `Error in router! Consider setting an 'errorComponent' in your RootRoute! 👍`);
267
+ }
268
+ }, /*#__PURE__*/React__namespace.createElement(Outlet, null)));
269
+ }
270
+ function useRouter() {
271
+ const value = React__namespace.useContext(routerContext);
272
+ warning__default["default"](value, 'useRouter must be used inside a <Router> component!');
273
+ return value;
274
+ }
275
+ function useMatches(opts) {
276
+ const matchIds = React__namespace.useContext(matchIdsContext);
277
+ return useRouterState({
278
+ select: state => {
279
+ const matches = state.renderedMatches.slice(state.renderedMatches.findIndex(d => d.id === matchIds[0]));
280
+ return opts?.select ? opts.select(matches) : matches;
281
+ }
282
+ });
283
+ }
284
+ function useMatch(opts) {
285
+ const router = useRouter();
286
+ const nearestMatchId = React__namespace.useContext(matchIdsContext)[0];
287
+ const nearestMatchRouteId = router.getRouteMatch(nearestMatchId)?.routeId;
288
+ const matchRouteId = useRouterState({
289
+ select: state => {
290
+ const match = opts?.from ? state.renderedMatches.find(d => d.routeId === opts?.from) : state.renderedMatches.find(d => d.id === nearestMatchId);
291
+ return match.routeId;
292
+ }
293
+ });
294
+ if (opts?.strict ?? true) {
295
+ invariant__default["default"](nearestMatchRouteId == matchRouteId, `useMatch("${matchRouteId}") is being called in a component that is meant to render the '${nearestMatchRouteId}' route. Did you mean to 'useMatch("${matchRouteId}", { strict: false })' or 'useRoute("${matchRouteId}")' instead?`);
296
+ }
297
+ const matchSelection = useRouterState({
298
+ select: state => {
299
+ const match = opts?.from ? state.renderedMatches.find(d => d.routeId === opts?.from) : state.renderedMatches.find(d => d.id === nearestMatchId);
300
+ invariant__default["default"](match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
301
+ return opts?.select ? opts.select(match) : match;
302
+ }
303
+ });
304
+ return matchSelection;
305
+ }
306
+ function useLoader(opts) {
307
+ return useMatch({
308
+ ...opts,
309
+ select: match => opts?.select ? opts?.select(match.loaderData) : match.loaderData
310
+ });
311
+ }
312
+ function useRouterContext(opts) {
313
+ return useMatch({
314
+ ...opts,
315
+ select: match => opts?.select ? opts.select(match.context) : match.context
316
+ });
317
+ }
318
+ function useRouteContext(opts) {
319
+ return useMatch({
320
+ ...opts,
321
+ select: match => opts?.select ? opts.select(match.context) : match.context
322
+ });
323
+ }
324
+ function useSearch(opts) {
325
+ return useMatch({
326
+ ...opts,
327
+ select: match => {
328
+ return opts?.select ? opts.select(match.search) : match.search;
329
+ }
330
+ });
331
+ }
332
+ function useParams(opts) {
333
+ return useRouterState({
334
+ select: state => {
335
+ const params = routerCore.last(state.renderedMatches)?.params;
336
+ return opts?.select ? opts.select(params) : params;
337
+ }
338
+ });
339
+ }
340
+ function useNavigate(defaultOpts) {
341
+ const router = useRouter();
342
+ return React__namespace.useCallback(opts => {
343
+ return router.navigate({
344
+ ...defaultOpts,
345
+ ...opts
346
+ });
347
+ }, []);
348
+ }
349
+ function useMatchRoute() {
350
+ const router = useRouter();
351
+ return React__namespace.useCallback(opts => {
352
+ const {
353
+ pending,
354
+ caseSensitive,
355
+ ...rest
356
+ } = opts;
357
+ return router.matchRoute(rest, {
358
+ pending,
359
+ caseSensitive
360
+ });
361
+ }, []);
362
+ }
363
+ function MatchRoute(props) {
364
+ const matchRoute = useMatchRoute();
365
+ const params = matchRoute(props);
366
+ if (typeof props.children === 'function') {
367
+ return props.children(params);
368
+ }
369
+ return !!params ? props.children : null;
370
+ }
371
+ function Outlet() {
372
+ const matchIds = React__namespace.useContext(matchIdsContext).slice(1);
373
+ if (!matchIds[0]) {
374
+ return null;
375
+ }
376
+ return /*#__PURE__*/React__namespace.createElement(Match, {
377
+ matchIds: matchIds
378
+ });
379
+ }
380
+ const defaultPending = () => null;
381
+ function Match({
382
+ matchIds
383
+ }) {
384
+ const router = useRouter();
385
+ const matchId = matchIds[0];
386
+ const routeId = router.getRouteMatch(matchId).routeId;
387
+ const route = router.getRoute(routeId);
388
+ const locationKey = useRouterState({
389
+ select: s => s.resolvedLocation.state?.key
390
+ });
391
+ const PendingComponent = route.options.pendingComponent ?? router.options.defaultPendingComponent ?? defaultPending;
392
+ const routeErrorComponent = route.options.errorComponent ?? router.options.defaultErrorComponent ?? ErrorComponent;
393
+ const ResolvedSuspenseBoundary = route.options.wrapInSuspense ?? !route.isRoot ? React__namespace.Suspense : SafeFragment;
394
+ const ResolvedCatchBoundary = !!routeErrorComponent ? CatchBoundary : SafeFragment;
395
+ const errorComponent = React__namespace.useCallback(props => {
396
+ return /*#__PURE__*/React__namespace.createElement(routeErrorComponent, {
397
+ ...props,
398
+ useMatch: route.useMatch,
399
+ useRouteContext: route.useRouteContext,
400
+ useSearch: route.useSearch,
401
+ useParams: route.useParams
402
+ });
403
+ }, [route]);
404
+ return /*#__PURE__*/React__namespace.createElement(matchIdsContext.Provider, {
405
+ value: matchIds
406
+ }, /*#__PURE__*/React__namespace.createElement(ResolvedSuspenseBoundary, {
407
+ fallback: /*#__PURE__*/React__namespace.createElement(PendingComponent, {
408
+ useMatch: route.useMatch,
409
+ useRouteContext: route.useRouteContext,
410
+ useSearch: route.useSearch,
411
+ useParams: route.useParams
412
+ })
413
+ }, /*#__PURE__*/React__namespace.createElement(ResolvedCatchBoundary, {
414
+ resetKey: locationKey,
415
+ errorComponent: errorComponent,
416
+ onCatch: () => {
417
+ warning__default["default"](false, `Error in route match: ${matchId}`);
418
+ }
419
+ }, /*#__PURE__*/React__namespace.createElement(MatchInner, {
420
+ matchId: matchId,
421
+ PendingComponent: PendingComponent
422
+ }))));
423
+ }
424
+ function MatchInner({
425
+ matchId,
426
+ PendingComponent
427
+ }) {
428
+ const router = useRouter();
429
+ const match = useRouterState({
430
+ select: d => {
431
+ const match = d.matchesById[matchId];
432
+ return routerCore.pick(match, ['status', 'loadPromise', 'routeId', 'error']);
433
+ }
434
+ });
435
+ const route = router.getRoute(match.routeId);
436
+ if (match.status === 'error') {
437
+ throw match.error;
438
+ }
439
+ if (match.status === 'pending') {
440
+ return /*#__PURE__*/React__namespace.createElement(PendingComponent, {
441
+ useLoader: route.useLoader,
442
+ useMatch: route.useMatch,
443
+ useRouteContext: route.useRouteContext,
444
+ useSearch: route.useSearch,
445
+ useParams: route.useParams
446
+ });
447
+ }
448
+ if (match.status === 'success') {
449
+ let comp = route.options.component ?? router.options.defaultComponent;
450
+ if (comp) {
451
+ return /*#__PURE__*/React__namespace.createElement(comp, {
452
+ useLoader: route.useLoader,
453
+ useMatch: route.useMatch,
454
+ useRouteContext: route.useRouteContext,
455
+ useSearch: route.useSearch,
456
+ useParams: route.useParams
457
+ });
458
+ }
459
+ return /*#__PURE__*/React__namespace.createElement(Outlet, null);
460
+ }
461
+ invariant__default["default"](false, 'Idle routeMatch status encountered during rendering! You should never see this. File an issue!');
462
+ }
463
+ function SafeFragment(props) {
464
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, props.children);
465
+ }
466
+ function useInjectHtml() {
467
+ const router = useRouter();
468
+ return React__namespace.useCallback(html => {
469
+ router.injectHtml(html);
470
+ }, []);
471
+ }
472
+ function useDehydrate() {
473
+ const router = useRouter();
474
+ return React__namespace.useCallback(function dehydrate(key, data) {
475
+ return router.dehydrateData(key, data);
476
+ }, []);
477
+ }
478
+ function useHydrate() {
479
+ const router = useRouter();
480
+ return function hydrate(key) {
481
+ return router.hydrateData(key);
482
+ };
483
+ }
484
+
485
+ // This is the messiest thing ever... I'm either seriously tired (likely) or
486
+ // there has to be a better way to reset error boundaries when the
487
+ // router's location key changes.
488
+
489
+ function CatchBoundary(props) {
490
+ const errorComponent = props.errorComponent ?? ErrorComponent;
491
+ return /*#__PURE__*/React__namespace.createElement(CatchBoundaryImpl, {
492
+ resetKey: props.resetKey,
493
+ onCatch: props.onCatch,
494
+ children: ({
495
+ error
496
+ }) => {
497
+ if (error) {
498
+ return /*#__PURE__*/React__namespace.createElement(errorComponent, {
499
+ error
500
+ });
501
+ }
502
+ return props.children;
503
+ }
504
+ });
505
+ }
506
+ class CatchBoundaryImpl extends React__namespace.Component {
507
+ state = {
508
+ error: null
509
+ };
510
+ static getDerivedStateFromError(error) {
511
+ return {
512
+ error
513
+ };
514
+ }
515
+ componentDidUpdate(prevProps, prevState) {
516
+ if (prevState.error && prevProps.resetKey !== this.props.resetKey) {
517
+ this.setState({
518
+ error: null
519
+ });
520
+ }
521
+ }
522
+ componentDidCatch(error) {
523
+ this.props.onCatch?.(error);
524
+ }
525
+ render() {
526
+ return this.props.children(this.state);
527
+ }
528
+ }
529
+ function ErrorComponent({
530
+ error
531
+ }) {
532
+ const [show, setShow] = React__namespace.useState(process.env.NODE_ENV !== 'production');
533
+ return /*#__PURE__*/React__namespace.createElement("div", {
534
+ style: {
535
+ padding: '.5rem',
536
+ maxWidth: '100%'
537
+ }
538
+ }, /*#__PURE__*/React__namespace.createElement("div", {
539
+ style: {
540
+ display: 'flex',
541
+ alignItems: 'center',
542
+ gap: '.5rem'
543
+ }
544
+ }, /*#__PURE__*/React__namespace.createElement("strong", {
545
+ style: {
546
+ fontSize: '1rem'
547
+ }
548
+ }, "Something went wrong!"), /*#__PURE__*/React__namespace.createElement("button", {
549
+ style: {
550
+ appearance: 'none',
551
+ fontSize: '.6em',
552
+ border: '1px solid currentColor',
553
+ padding: '.1rem .2rem',
554
+ fontWeight: 'bold',
555
+ borderRadius: '.25rem'
556
+ },
557
+ onClick: () => setShow(d => !d)
558
+ }, show ? 'Hide Error' : 'Show Error')), /*#__PURE__*/React__namespace.createElement("div", {
559
+ style: {
560
+ height: '.25rem'
561
+ }
562
+ }), show ? /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("pre", {
563
+ style: {
564
+ fontSize: '.7em',
565
+ border: '1px solid red',
566
+ borderRadius: '.25rem',
567
+ padding: '.3rem',
568
+ color: 'red',
569
+ overflow: 'auto'
570
+ }
571
+ }, error.message ? /*#__PURE__*/React__namespace.createElement("code", null, error.message) : null)) : null);
572
+ }
573
+ function useBlocker(message, condition = true) {
574
+ const router = useRouter();
575
+ React__namespace.useEffect(() => {
576
+ if (!condition) return;
577
+ let unblock = router.history.block((retry, cancel) => {
578
+ if (window.confirm(message)) {
579
+ unblock();
580
+ retry();
581
+ }
582
+ });
583
+ return unblock;
584
+ });
585
+ }
586
+ function Block({
587
+ message,
588
+ condition,
589
+ children
590
+ }) {
591
+ useBlocker(message, condition);
592
+ return children ?? null;
593
+ }
594
+ function shallow(objA, objB) {
595
+ if (Object.is(objA, objB)) {
596
+ return true;
597
+ }
598
+ if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
599
+ return false;
600
+ }
601
+ const keysA = Object.keys(objA);
602
+ if (keysA.length !== Object.keys(objB).length) {
603
+ return false;
604
+ }
605
+ for (let i = 0; i < keysA.length; i++) {
606
+ if (!Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !Object.is(objA[keysA[i]], objB[keysA[i]])) {
607
+ return false;
608
+ }
609
+ }
610
+ return true;
611
+ }
612
+
613
+ exports.Block = Block;
614
+ exports.CatchBoundary = CatchBoundary;
615
+ exports.CatchBoundaryImpl = CatchBoundaryImpl;
616
+ exports.ErrorComponent = ErrorComponent;
617
+ exports.Link = Link;
618
+ exports.MatchRoute = MatchRoute;
619
+ exports.Navigate = Navigate;
620
+ exports.Outlet = Outlet;
621
+ exports.RouterProvider = RouterProvider;
622
+ exports.lazyRouteComponent = lazyRouteComponent;
623
+ exports.matchIdsContext = matchIdsContext;
624
+ exports.routerContext = routerContext;
625
+ exports.shallow = shallow;
626
+ exports.useBlocker = useBlocker;
627
+ exports.useDehydrate = useDehydrate;
628
+ exports.useHydrate = useHydrate;
629
+ exports.useInjectHtml = useInjectHtml;
630
+ exports.useLinkProps = useLinkProps;
631
+ exports.useLoader = useLoader;
632
+ exports.useMatch = useMatch;
633
+ exports.useMatchRoute = useMatchRoute;
634
+ exports.useMatches = useMatches;
635
+ exports.useNavigate = useNavigate;
636
+ exports.useParams = useParams;
637
+ exports.useRouteContext = useRouteContext;
638
+ exports.useRouter = useRouter;
639
+ exports.useRouterContext = useRouterContext;
640
+ exports.useRouterState = useRouterState;
641
+ exports.useSearch = useSearch;
642
+ //# sourceMappingURL=react.js.map