@tinkoff/router 0.3.1 → 0.3.8

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.
Files changed (36) hide show
  1. package/lib/components/react/context.browser.js +4 -1
  2. package/lib/components/react/context.d.ts +8 -0
  3. package/lib/components/react/context.es.js +4 -1
  4. package/lib/components/react/context.js +4 -0
  5. package/lib/components/react/index.browser.js +12 -0
  6. package/lib/components/react/index.d.ts +3 -1
  7. package/lib/components/react/index.es.js +12 -0
  8. package/lib/components/react/index.js +16 -0
  9. package/lib/components/react/providers/default.browser.js +17 -0
  10. package/lib/components/react/providers/default.d.ts +4 -0
  11. package/lib/components/react/providers/default.es.js +17 -0
  12. package/lib/components/react/providers/default.js +21 -0
  13. package/lib/components/react/providers/transitions.browser.js +89 -0
  14. package/lib/components/react/providers/transitions.d.ts +5 -0
  15. package/lib/components/react/providers/transitions.es.js +89 -0
  16. package/lib/components/react/providers/transitions.js +94 -0
  17. package/lib/components/react/providers/types.d.ts +16 -0
  18. package/lib/components/react/useViewTransition.browser.js +22 -0
  19. package/lib/components/react/useViewTransition.d.ts +5 -0
  20. package/lib/components/react/useViewTransition.es.js +22 -0
  21. package/lib/components/react/useViewTransition.js +26 -0
  22. package/lib/index.browser.js +4 -3
  23. package/lib/index.es.js +4 -3
  24. package/lib/index.js +10 -8
  25. package/lib/router/abstract.browser.js +8 -3
  26. package/lib/router/abstract.d.ts +3 -1
  27. package/lib/router/abstract.es.js +8 -3
  28. package/lib/router/abstract.js +8 -3
  29. package/lib/router/browser.browser.js +66 -1
  30. package/lib/router/browser.d.ts +5 -1
  31. package/lib/types.d.ts +5 -0
  32. package/package.json +2 -1
  33. package/lib/components/react/provider.browser.js +0 -16
  34. package/lib/components/react/provider.d.ts +0 -13
  35. package/lib/components/react/provider.es.js +0 -16
  36. package/lib/components/react/provider.js +0 -20
@@ -3,5 +3,8 @@ import { createContext } from 'react';
3
3
  const RouterContext = createContext(null);
4
4
  const RouteContext = createContext(null);
5
5
  const UrlContext = createContext(null);
6
+ const ViewTransitionContext = createContext({
7
+ isTransitioning: false,
8
+ });
6
9
 
7
- export { RouteContext, RouterContext, UrlContext };
10
+ export { RouteContext, RouterContext, UrlContext, ViewTransitionContext };
@@ -4,4 +4,12 @@ import type { NavigationRoute } from '../../types';
4
4
  export declare const RouterContext: import("react").Context<AbstractRouter>;
5
5
  export declare const RouteContext: import("react").Context<NavigationRoute>;
6
6
  export declare const UrlContext: import("react").Context<Url>;
7
+ export type ViewTransitionState = {
8
+ isTransitioning: false;
9
+ } | {
10
+ isTransitioning: true;
11
+ currentRoute: NavigationRoute;
12
+ nextRoute: NavigationRoute;
13
+ };
14
+ export declare const ViewTransitionContext: import("react").Context<ViewTransitionState>;
7
15
  //# sourceMappingURL=context.d.ts.map
@@ -3,5 +3,8 @@ import { createContext } from 'react';
3
3
  const RouterContext = createContext(null);
4
4
  const RouteContext = createContext(null);
5
5
  const UrlContext = createContext(null);
6
+ const ViewTransitionContext = createContext({
7
+ isTransitioning: false,
8
+ });
6
9
 
7
- export { RouteContext, RouterContext, UrlContext };
10
+ export { RouteContext, RouterContext, UrlContext, ViewTransitionContext };
@@ -7,7 +7,11 @@ var react = require('react');
7
7
  const RouterContext = react.createContext(null);
8
8
  const RouteContext = react.createContext(null);
9
9
  const UrlContext = react.createContext(null);
10
+ const ViewTransitionContext = react.createContext({
11
+ isTransitioning: false,
12
+ });
10
13
 
11
14
  exports.RouteContext = RouteContext;
12
15
  exports.RouterContext = RouterContext;
13
16
  exports.UrlContext = UrlContext;
17
+ exports.ViewTransitionContext = ViewTransitionContext;
@@ -0,0 +1,12 @@
1
+ import { TransitionsProvider } from './providers/transitions.browser.js';
2
+ import { DefaultProvider } from './providers/default.browser.js';
3
+ import 'react';
4
+ import './context.browser.js';
5
+ import '@tinkoff/react-hooks';
6
+
7
+ const Provider = process.env.__TRAMVAI_VIEW_TRANSITIONS === 'true' ||
8
+ process.env.__TRAMVAI_REACT_TRANSITIONS === 'true'
9
+ ? TransitionsProvider
10
+ : DefaultProvider;
11
+
12
+ export { Provider };
@@ -1,6 +1,8 @@
1
- export { Provider } from './provider';
1
+ export type { RouterState } from './providers/types';
2
2
  export { useRouter } from './useRouter';
3
3
  export { useRoute } from './useRoute';
4
4
  export { useUrl } from './useUrl';
5
5
  export { useNavigate } from './useNavigate';
6
+ export { useViewTransition } from './useViewTransition';
7
+ export declare const Provider: import("react").FunctionComponent<import("./providers/types").RouterProviderProps>;
6
8
  //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,12 @@
1
+ import { TransitionsProvider } from './providers/transitions.es.js';
2
+ import { DefaultProvider } from './providers/default.es.js';
3
+ import 'react';
4
+ import './context.es.js';
5
+ import '@tinkoff/react-hooks';
6
+
7
+ const Provider = process.env.__TRAMVAI_VIEW_TRANSITIONS === 'true' ||
8
+ process.env.__TRAMVAI_REACT_TRANSITIONS === 'true'
9
+ ? TransitionsProvider
10
+ : DefaultProvider;
11
+
12
+ export { Provider };
@@ -0,0 +1,16 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var transitions = require('./providers/transitions.js');
6
+ var _default = require('./providers/default.js');
7
+ require('react');
8
+ require('./context.js');
9
+ require('@tinkoff/react-hooks');
10
+
11
+ const Provider = process.env.__TRAMVAI_VIEW_TRANSITIONS === 'true' ||
12
+ process.env.__TRAMVAI_REACT_TRANSITIONS === 'true'
13
+ ? transitions.TransitionsProvider
14
+ : _default.DefaultProvider;
15
+
16
+ exports.Provider = Provider;
@@ -0,0 +1,17 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useMemo, useCallback } from 'react';
3
+ import { useSyncExternalStore } from 'use-sync-external-store/shim';
4
+ import { RouterContext, RouteContext, UrlContext } from '../context.browser.js';
5
+
6
+ const DefaultProvider = ({ router, children, serverState, }) => {
7
+ // fallback to outdated router implementation
8
+ const lastRoute = useMemo(() => router.getCurrentRoute(), [router]);
9
+ const lastUrl = useMemo(() => router.getCurrentUrl(), [router]);
10
+ const subscription = useCallback((cb) => router.registerSyncHook('change', cb), [router]);
11
+ const route = useSyncExternalStore(subscription, () => { var _a, _b; return (_b = (_a = router.getLastRoute) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastRoute; }, serverState ? () => serverState.route : () => { var _a, _b; return (_b = (_a = router.getLastRoute) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastRoute; });
12
+ const url = useSyncExternalStore(subscription, () => { var _a, _b; return (_b = (_a = router.getLastUrl) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastUrl; }, serverState ? () => serverState.url : () => { var _a, _b; return (_b = (_a = router.getLastUrl) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastUrl; });
13
+ return (jsx(RouterContext.Provider, { value: router, children: jsx(RouteContext.Provider, { value: route, children: jsx(UrlContext.Provider, { value: url, children: children }) }) }));
14
+ };
15
+ DefaultProvider.displayName = 'TinkoffRouterProvider';
16
+
17
+ export { DefaultProvider };
@@ -0,0 +1,4 @@
1
+ import type { FunctionComponent } from 'react';
2
+ import type { RouterProviderProps } from './types';
3
+ export declare const DefaultProvider: FunctionComponent<RouterProviderProps>;
4
+ //# sourceMappingURL=default.d.ts.map
@@ -0,0 +1,17 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useMemo, useCallback } from 'react';
3
+ import { useSyncExternalStore } from 'use-sync-external-store/shim';
4
+ import { RouterContext, RouteContext, UrlContext } from '../context.es.js';
5
+
6
+ const DefaultProvider = ({ router, children, serverState, }) => {
7
+ // fallback to outdated router implementation
8
+ const lastRoute = useMemo(() => router.getCurrentRoute(), [router]);
9
+ const lastUrl = useMemo(() => router.getCurrentUrl(), [router]);
10
+ const subscription = useCallback((cb) => router.registerSyncHook('change', cb), [router]);
11
+ const route = useSyncExternalStore(subscription, () => { var _a, _b; return (_b = (_a = router.getLastRoute) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastRoute; }, serverState ? () => serverState.route : () => { var _a, _b; return (_b = (_a = router.getLastRoute) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastRoute; });
12
+ const url = useSyncExternalStore(subscription, () => { var _a, _b; return (_b = (_a = router.getLastUrl) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastUrl; }, serverState ? () => serverState.url : () => { var _a, _b; return (_b = (_a = router.getLastUrl) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastUrl; });
13
+ return (jsx(RouterContext.Provider, { value: router, children: jsx(RouteContext.Provider, { value: route, children: jsx(UrlContext.Provider, { value: url, children: children }) }) }));
14
+ };
15
+ DefaultProvider.displayName = 'TinkoffRouterProvider';
16
+
17
+ export { DefaultProvider };
@@ -0,0 +1,21 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var react = require('react');
7
+ var shim = require('use-sync-external-store/shim');
8
+ var context = require('../context.js');
9
+
10
+ const DefaultProvider = ({ router, children, serverState, }) => {
11
+ // fallback to outdated router implementation
12
+ const lastRoute = react.useMemo(() => router.getCurrentRoute(), [router]);
13
+ const lastUrl = react.useMemo(() => router.getCurrentUrl(), [router]);
14
+ const subscription = react.useCallback((cb) => router.registerSyncHook('change', cb), [router]);
15
+ const route = shim.useSyncExternalStore(subscription, () => { var _a, _b; return (_b = (_a = router.getLastRoute) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastRoute; }, serverState ? () => serverState.route : () => { var _a, _b; return (_b = (_a = router.getLastRoute) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastRoute; });
16
+ const url = shim.useSyncExternalStore(subscription, () => { var _a, _b; return (_b = (_a = router.getLastUrl) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastUrl; }, serverState ? () => serverState.url : () => { var _a, _b; return (_b = (_a = router.getLastUrl) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastUrl; });
17
+ return (jsxRuntime.jsx(context.RouterContext.Provider, { value: router, children: jsxRuntime.jsx(context.RouteContext.Provider, { value: route, children: jsxRuntime.jsx(context.UrlContext.Provider, { value: url, children: children }) }) }));
18
+ };
19
+ DefaultProvider.displayName = 'TinkoffRouterProvider';
20
+
21
+ exports.DefaultProvider = DefaultProvider;
@@ -0,0 +1,89 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useState, useEffect } from 'react';
3
+ import { useIsomorphicLayoutEffect } from '@tinkoff/react-hooks';
4
+ import { Deferred } from '@tramvai/core';
5
+ import { RouterContext, RouteContext, UrlContext, ViewTransitionContext } from '../context.browser.js';
6
+
7
+ let startTransition;
8
+ try {
9
+ // eslint-disable-next-line import/no-unresolved, import/extensions
10
+ startTransition = require('react').startTransition;
11
+ }
12
+ catch { }
13
+ const startReactTransition = typeof startTransition === 'function' && process.env.__TRAMVAI_REACT_TRANSITIONS === 'true'
14
+ ? startTransition
15
+ : (fn) => {
16
+ fn();
17
+ };
18
+ const TransitionsProvider = ({ router, children, }) => {
19
+ // fallback to outdated router implementation
20
+ const [state, setState] = useState(() => {
21
+ var _a, _b;
22
+ return ({
23
+ route: (_a = router.getLastRoute()) !== null && _a !== void 0 ? _a : router.getCurrentRoute(),
24
+ url: (_b = router.getLastUrl()) !== null && _b !== void 0 ? _b : router.getCurrentUrl(),
25
+ });
26
+ });
27
+ // Router state that will be applied to the DOM.
28
+ const [pendingState, setPendingState] = useState(null);
29
+ // View transition state for context.
30
+ const [viewTransitionState, setViewTransitionState] = useState({
31
+ isTransitioning: false,
32
+ });
33
+ // Deferred render promise, that resolves when the DOM updates after a navigation.
34
+ const [renderDeferred, setRenderDeferred] = useState(null);
35
+ const updateRouterState = (navigation) => {
36
+ if (document.startViewTransition !== undefined && navigation.viewTransition) {
37
+ setPendingState({ route: navigation.to, url: navigation.url });
38
+ setViewTransitionState({
39
+ isTransitioning: true,
40
+ currentRoute: navigation.from,
41
+ nextRoute: navigation.to,
42
+ });
43
+ return;
44
+ }
45
+ startReactTransition(() => {
46
+ setState({ route: navigation.to, url: navigation.url });
47
+ });
48
+ };
49
+ useIsomorphicLayoutEffect(() => router.registerSyncHook('change', updateRouterState), [router]);
50
+ // It's okay to ignore rules of hooks here, because this code
51
+ // will not be a part of the bundle in case of truly condition
52
+ if (process.env.__TRAMVAI_VIEW_TRANSITIONS === 'true') {
53
+ // eslint-disable-next-line react-hooks/rules-of-hooks
54
+ useEffect(() => {
55
+ if (viewTransitionState.isTransitioning) {
56
+ setRenderDeferred(new Deferred());
57
+ }
58
+ }, [viewTransitionState.isTransitioning]);
59
+ // eslint-disable-next-line react-hooks/rules-of-hooks
60
+ useEffect(() => {
61
+ if (renderDeferred !== null && pendingState !== null) {
62
+ const transition = document.startViewTransition(async () => {
63
+ startReactTransition(() => {
64
+ setState(pendingState);
65
+ });
66
+ await renderDeferred.promise;
67
+ });
68
+ // eslint-disable-next-line promise/catch-or-return
69
+ transition.finished.finally(() => {
70
+ setRenderDeferred(null);
71
+ setPendingState(null);
72
+ setViewTransitionState({ isTransitioning: false });
73
+ });
74
+ }
75
+ }, [pendingState, renderDeferred]);
76
+ // eslint-disable-next-line react-hooks/rules-of-hooks
77
+ useEffect(() => {
78
+ if (renderDeferred !== null &&
79
+ pendingState !== null &&
80
+ state.route.actualPath === pendingState.route.actualPath) {
81
+ renderDeferred.resolve();
82
+ }
83
+ }, [state, renderDeferred, pendingState]);
84
+ }
85
+ return (jsx(RouterContext.Provider, { value: router, children: jsx(RouteContext.Provider, { value: state.route, children: jsx(UrlContext.Provider, { value: state.url, children: jsx(ViewTransitionContext.Provider, { value: viewTransitionState, children: children }) }) }) }));
86
+ };
87
+ TransitionsProvider.displayName = 'TinkoffRouterTransitionsProvider';
88
+
89
+ export { TransitionsProvider, startReactTransition };
@@ -0,0 +1,5 @@
1
+ import type { FunctionComponent } from 'react';
2
+ import type { RouterProviderProps } from './types';
3
+ export declare const startReactTransition: any;
4
+ export declare const TransitionsProvider: FunctionComponent<RouterProviderProps>;
5
+ //# sourceMappingURL=transitions.d.ts.map
@@ -0,0 +1,89 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useState, useEffect } from 'react';
3
+ import { useIsomorphicLayoutEffect } from '@tinkoff/react-hooks';
4
+ import { Deferred } from '@tramvai/core';
5
+ import { RouterContext, RouteContext, UrlContext, ViewTransitionContext } from '../context.es.js';
6
+
7
+ let startTransition;
8
+ try {
9
+ // eslint-disable-next-line import/no-unresolved, import/extensions
10
+ startTransition = require('react').startTransition;
11
+ }
12
+ catch { }
13
+ const startReactTransition = typeof startTransition === 'function' && process.env.__TRAMVAI_REACT_TRANSITIONS === 'true'
14
+ ? startTransition
15
+ : (fn) => {
16
+ fn();
17
+ };
18
+ const TransitionsProvider = ({ router, children, }) => {
19
+ // fallback to outdated router implementation
20
+ const [state, setState] = useState(() => {
21
+ var _a, _b;
22
+ return ({
23
+ route: (_a = router.getLastRoute()) !== null && _a !== void 0 ? _a : router.getCurrentRoute(),
24
+ url: (_b = router.getLastUrl()) !== null && _b !== void 0 ? _b : router.getCurrentUrl(),
25
+ });
26
+ });
27
+ // Router state that will be applied to the DOM.
28
+ const [pendingState, setPendingState] = useState(null);
29
+ // View transition state for context.
30
+ const [viewTransitionState, setViewTransitionState] = useState({
31
+ isTransitioning: false,
32
+ });
33
+ // Deferred render promise, that resolves when the DOM updates after a navigation.
34
+ const [renderDeferred, setRenderDeferred] = useState(null);
35
+ const updateRouterState = (navigation) => {
36
+ if (document.startViewTransition !== undefined && navigation.viewTransition) {
37
+ setPendingState({ route: navigation.to, url: navigation.url });
38
+ setViewTransitionState({
39
+ isTransitioning: true,
40
+ currentRoute: navigation.from,
41
+ nextRoute: navigation.to,
42
+ });
43
+ return;
44
+ }
45
+ startReactTransition(() => {
46
+ setState({ route: navigation.to, url: navigation.url });
47
+ });
48
+ };
49
+ useIsomorphicLayoutEffect(() => router.registerSyncHook('change', updateRouterState), [router]);
50
+ // It's okay to ignore rules of hooks here, because this code
51
+ // will not be a part of the bundle in case of truly condition
52
+ if (process.env.__TRAMVAI_VIEW_TRANSITIONS === 'true') {
53
+ // eslint-disable-next-line react-hooks/rules-of-hooks
54
+ useEffect(() => {
55
+ if (viewTransitionState.isTransitioning) {
56
+ setRenderDeferred(new Deferred());
57
+ }
58
+ }, [viewTransitionState.isTransitioning]);
59
+ // eslint-disable-next-line react-hooks/rules-of-hooks
60
+ useEffect(() => {
61
+ if (renderDeferred !== null && pendingState !== null) {
62
+ const transition = document.startViewTransition(async () => {
63
+ startReactTransition(() => {
64
+ setState(pendingState);
65
+ });
66
+ await renderDeferred.promise;
67
+ });
68
+ // eslint-disable-next-line promise/catch-or-return
69
+ transition.finished.finally(() => {
70
+ setRenderDeferred(null);
71
+ setPendingState(null);
72
+ setViewTransitionState({ isTransitioning: false });
73
+ });
74
+ }
75
+ }, [pendingState, renderDeferred]);
76
+ // eslint-disable-next-line react-hooks/rules-of-hooks
77
+ useEffect(() => {
78
+ if (renderDeferred !== null &&
79
+ pendingState !== null &&
80
+ state.route.actualPath === pendingState.route.actualPath) {
81
+ renderDeferred.resolve();
82
+ }
83
+ }, [state, renderDeferred, pendingState]);
84
+ }
85
+ return (jsx(RouterContext.Provider, { value: router, children: jsx(RouteContext.Provider, { value: state.route, children: jsx(UrlContext.Provider, { value: state.url, children: jsx(ViewTransitionContext.Provider, { value: viewTransitionState, children: children }) }) }) }));
86
+ };
87
+ TransitionsProvider.displayName = 'TinkoffRouterTransitionsProvider';
88
+
89
+ export { TransitionsProvider, startReactTransition };
@@ -0,0 +1,94 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var react = require('react');
7
+ var reactHooks = require('@tinkoff/react-hooks');
8
+ var core = require('@tramvai/core');
9
+ var context = require('../context.js');
10
+
11
+ let startTransition;
12
+ try {
13
+ // eslint-disable-next-line import/no-unresolved, import/extensions
14
+ startTransition = require('react').startTransition;
15
+ }
16
+ catch { }
17
+ const startReactTransition = typeof startTransition === 'function' && process.env.__TRAMVAI_REACT_TRANSITIONS === 'true'
18
+ ? startTransition
19
+ : (fn) => {
20
+ fn();
21
+ };
22
+ const TransitionsProvider = ({ router, children, }) => {
23
+ // fallback to outdated router implementation
24
+ const [state, setState] = react.useState(() => {
25
+ var _a, _b;
26
+ return ({
27
+ route: (_a = router.getLastRoute()) !== null && _a !== void 0 ? _a : router.getCurrentRoute(),
28
+ url: (_b = router.getLastUrl()) !== null && _b !== void 0 ? _b : router.getCurrentUrl(),
29
+ });
30
+ });
31
+ // Router state that will be applied to the DOM.
32
+ const [pendingState, setPendingState] = react.useState(null);
33
+ // View transition state for context.
34
+ const [viewTransitionState, setViewTransitionState] = react.useState({
35
+ isTransitioning: false,
36
+ });
37
+ // Deferred render promise, that resolves when the DOM updates after a navigation.
38
+ const [renderDeferred, setRenderDeferred] = react.useState(null);
39
+ const updateRouterState = (navigation) => {
40
+ if (document.startViewTransition !== undefined && navigation.viewTransition) {
41
+ setPendingState({ route: navigation.to, url: navigation.url });
42
+ setViewTransitionState({
43
+ isTransitioning: true,
44
+ currentRoute: navigation.from,
45
+ nextRoute: navigation.to,
46
+ });
47
+ return;
48
+ }
49
+ startReactTransition(() => {
50
+ setState({ route: navigation.to, url: navigation.url });
51
+ });
52
+ };
53
+ reactHooks.useIsomorphicLayoutEffect(() => router.registerSyncHook('change', updateRouterState), [router]);
54
+ // It's okay to ignore rules of hooks here, because this code
55
+ // will not be a part of the bundle in case of truly condition
56
+ if (process.env.__TRAMVAI_VIEW_TRANSITIONS === 'true') {
57
+ // eslint-disable-next-line react-hooks/rules-of-hooks
58
+ react.useEffect(() => {
59
+ if (viewTransitionState.isTransitioning) {
60
+ setRenderDeferred(new core.Deferred());
61
+ }
62
+ }, [viewTransitionState.isTransitioning]);
63
+ // eslint-disable-next-line react-hooks/rules-of-hooks
64
+ react.useEffect(() => {
65
+ if (renderDeferred !== null && pendingState !== null) {
66
+ const transition = document.startViewTransition(async () => {
67
+ startReactTransition(() => {
68
+ setState(pendingState);
69
+ });
70
+ await renderDeferred.promise;
71
+ });
72
+ // eslint-disable-next-line promise/catch-or-return
73
+ transition.finished.finally(() => {
74
+ setRenderDeferred(null);
75
+ setPendingState(null);
76
+ setViewTransitionState({ isTransitioning: false });
77
+ });
78
+ }
79
+ }, [pendingState, renderDeferred]);
80
+ // eslint-disable-next-line react-hooks/rules-of-hooks
81
+ react.useEffect(() => {
82
+ if (renderDeferred !== null &&
83
+ pendingState !== null &&
84
+ state.route.actualPath === pendingState.route.actualPath) {
85
+ renderDeferred.resolve();
86
+ }
87
+ }, [state, renderDeferred, pendingState]);
88
+ }
89
+ return (jsxRuntime.jsx(context.RouterContext.Provider, { value: router, children: jsxRuntime.jsx(context.RouteContext.Provider, { value: state.route, children: jsxRuntime.jsx(context.UrlContext.Provider, { value: state.url, children: jsxRuntime.jsx(context.ViewTransitionContext.Provider, { value: viewTransitionState, children: children }) }) }) }));
90
+ };
91
+ TransitionsProvider.displayName = 'TinkoffRouterTransitionsProvider';
92
+
93
+ exports.TransitionsProvider = TransitionsProvider;
94
+ exports.startReactTransition = startReactTransition;
@@ -0,0 +1,16 @@
1
+ import type { Url } from '@tinkoff/url';
2
+ import type { PropsWithChildren } from 'react';
3
+ import type { NavigationRoute } from '../../../types';
4
+ import type { AbstractRouter } from '../../../router/abstract';
5
+ export interface RouterState {
6
+ route: NavigationRoute;
7
+ url: Url;
8
+ }
9
+ export interface RouterProviderProps extends PropsWithChildren {
10
+ router: AbstractRouter;
11
+ serverState?: {
12
+ route: NavigationRoute;
13
+ url: Url;
14
+ };
15
+ }
16
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,22 @@
1
+ import { useContext } from 'react';
2
+ import { ViewTransitionContext, RouterContext } from './context.browser.js';
3
+
4
+ const normalizePath = (payload, router) => {
5
+ const route = router.resolve(payload);
6
+ const resolvedPath = route.redirect !== undefined ? router.resolve(route.redirect).actualPath : route.actualPath;
7
+ return resolvedPath.endsWith('/') ? resolvedPath : `${resolvedPath}/`;
8
+ };
9
+ const useViewTransition = (payload) => {
10
+ const viewTransition = useContext(ViewTransitionContext);
11
+ const router = useContext(RouterContext);
12
+ if (viewTransition.isTransitioning) {
13
+ const path = normalizePath(payload, router);
14
+ const currentPath = viewTransition.currentRoute.actualPath;
15
+ const nextPath = viewTransition.nextRoute.actualPath;
16
+ // We are handling forward and back navigations for the same route here
17
+ return [currentPath, nextPath].includes(path);
18
+ }
19
+ return false;
20
+ };
21
+
22
+ export { useViewTransition };
@@ -0,0 +1,5 @@
1
+ import type { NavigateOptions } from '../../types';
2
+ type Payload = string | Partial<NavigateOptions>;
3
+ export declare const useViewTransition: (payload: Payload) => boolean;
4
+ export {};
5
+ //# sourceMappingURL=useViewTransition.d.ts.map
@@ -0,0 +1,22 @@
1
+ import { useContext } from 'react';
2
+ import { ViewTransitionContext, RouterContext } from './context.es.js';
3
+
4
+ const normalizePath = (payload, router) => {
5
+ const route = router.resolve(payload);
6
+ const resolvedPath = route.redirect !== undefined ? router.resolve(route.redirect).actualPath : route.actualPath;
7
+ return resolvedPath.endsWith('/') ? resolvedPath : `${resolvedPath}/`;
8
+ };
9
+ const useViewTransition = (payload) => {
10
+ const viewTransition = useContext(ViewTransitionContext);
11
+ const router = useContext(RouterContext);
12
+ if (viewTransition.isTransitioning) {
13
+ const path = normalizePath(payload, router);
14
+ const currentPath = viewTransition.currentRoute.actualPath;
15
+ const nextPath = viewTransition.nextRoute.actualPath;
16
+ // We are handling forward and back navigations for the same route here
17
+ return [currentPath, nextPath].includes(path);
18
+ }
19
+ return false;
20
+ };
21
+
22
+ export { useViewTransition };
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var react = require('react');
6
+ var context = require('./context.js');
7
+
8
+ const normalizePath = (payload, router) => {
9
+ const route = router.resolve(payload);
10
+ const resolvedPath = route.redirect !== undefined ? router.resolve(route.redirect).actualPath : route.actualPath;
11
+ return resolvedPath.endsWith('/') ? resolvedPath : `${resolvedPath}/`;
12
+ };
13
+ const useViewTransition = (payload) => {
14
+ const viewTransition = react.useContext(context.ViewTransitionContext);
15
+ const router = react.useContext(context.RouterContext);
16
+ if (viewTransition.isTransitioning) {
17
+ const path = normalizePath(payload, router);
18
+ const currentPath = viewTransition.currentRoute.actualPath;
19
+ const nextPath = viewTransition.nextRoute.actualPath;
20
+ // We are handling forward and back navigations for the same route here
21
+ return [currentPath, nextPath].includes(path);
22
+ }
23
+ return false;
24
+ };
25
+
26
+ exports.useViewTransition = useViewTransition;
@@ -3,10 +3,11 @@ export { NoSpaRouter } from './router/clientNoSpa.browser.js';
3
3
  export { AbstractRouter } from './router/abstract.browser.js';
4
4
  export { History } from './history/base.browser.js';
5
5
  export { logger, setLogger } from './logger.browser.js';
6
- export { Provider } from './components/react/provider.browser.js';
6
+ export { Provider } from './components/react/index.browser.js';
7
+ export { RouteTree } from './tree/tree.browser.js';
8
+ export { getParts, isHistoryFallback, isParameterized, isWildcard, makePath, parse } from './tree/utils.browser.js';
7
9
  export { useRouter } from './components/react/useRouter.browser.js';
8
10
  export { useRoute } from './components/react/useRoute.browser.js';
9
11
  export { useUrl } from './components/react/useUrl.browser.js';
10
12
  export { useNavigate } from './components/react/useNavigate.browser.js';
11
- export { RouteTree } from './tree/tree.browser.js';
12
- export { getParts, isHistoryFallback, isParameterized, isWildcard, makePath, parse } from './tree/utils.browser.js';
13
+ export { useViewTransition } from './components/react/useViewTransition.browser.js';
package/lib/index.es.js CHANGED
@@ -3,10 +3,11 @@ export { NoSpaRouter } from './router/clientNoSpa.es.js';
3
3
  export { AbstractRouter } from './router/abstract.es.js';
4
4
  export { History } from './history/base.es.js';
5
5
  export { logger, setLogger } from './logger.es.js';
6
- export { Provider } from './components/react/provider.es.js';
6
+ export { Provider } from './components/react/index.es.js';
7
+ export { RouteTree } from './tree/tree.es.js';
8
+ export { getParts, isHistoryFallback, isParameterized, isWildcard, makePath, parse } from './tree/utils.es.js';
7
9
  export { useRouter } from './components/react/useRouter.es.js';
8
10
  export { useRoute } from './components/react/useRoute.es.js';
9
11
  export { useUrl } from './components/react/useUrl.es.js';
10
12
  export { useNavigate } from './components/react/useNavigate.es.js';
11
- export { RouteTree } from './tree/tree.es.js';
12
- export { getParts, isHistoryFallback, isParameterized, isWildcard, makePath, parse } from './tree/utils.es.js';
13
+ export { useViewTransition } from './components/react/useViewTransition.es.js';
package/lib/index.js CHANGED
@@ -7,13 +7,14 @@ var clientNoSpa = require('./router/clientNoSpa.js');
7
7
  var abstract = require('./router/abstract.js');
8
8
  var base = require('./history/base.js');
9
9
  var logger = require('./logger.js');
10
- var provider = require('./components/react/provider.js');
10
+ var index = require('./components/react/index.js');
11
+ var tree = require('./tree/tree.js');
12
+ var utils = require('./tree/utils.js');
11
13
  var useRouter = require('./components/react/useRouter.js');
12
14
  var useRoute = require('./components/react/useRoute.js');
13
15
  var useUrl = require('./components/react/useUrl.js');
14
16
  var useNavigate = require('./components/react/useNavigate.js');
15
- var tree = require('./tree/tree.js');
16
- var utils = require('./tree/utils.js');
17
+ var useViewTransition = require('./components/react/useViewTransition.js');
17
18
 
18
19
 
19
20
 
@@ -26,11 +27,7 @@ Object.defineProperty(exports, 'logger', {
26
27
  get: function () { return logger.logger; }
27
28
  });
28
29
  exports.setLogger = logger.setLogger;
29
- exports.Provider = provider.Provider;
30
- exports.useRouter = useRouter.useRouter;
31
- exports.useRoute = useRoute.useRoute;
32
- exports.useUrl = useUrl.useUrl;
33
- exports.useNavigate = useNavigate.useNavigate;
30
+ exports.Provider = index.Provider;
34
31
  exports.RouteTree = tree.RouteTree;
35
32
  exports.getParts = utils.getParts;
36
33
  exports.isHistoryFallback = utils.isHistoryFallback;
@@ -38,3 +35,8 @@ exports.isParameterized = utils.isParameterized;
38
35
  exports.isWildcard = utils.isWildcard;
39
36
  exports.makePath = utils.makePath;
40
37
  exports.parse = utils.parse;
38
+ exports.useRouter = useRouter.useRouter;
39
+ exports.useRoute = useRoute.useRoute;
40
+ exports.useUrl = useUrl.useUrl;
41
+ exports.useNavigate = useNavigate.useNavigate;
42
+ exports.useViewTransition = useViewTransition.useViewTransition;
@@ -6,14 +6,16 @@ import { logger } from '../logger.browser.js';
6
6
  import { makeNavigateOptions, normalizeManySlashes, normalizeTrailingSlash, isSameHost, registerHook } from '../utils.browser.js';
7
7
 
8
8
  class AbstractRouter {
9
- constructor({ trailingSlash, mergeSlashes, beforeResolve = [], beforeNavigate = [], afterNavigate = [], beforeUpdateCurrent = [], afterUpdateCurrent = [], guards = [], onChange = [], onRedirect, onNotFound, onBlock, }) {
9
+ constructor({ trailingSlash, mergeSlashes, enableViewTransitions, beforeResolve = [], beforeNavigate = [], afterNavigate = [], beforeUpdateCurrent = [], afterUpdateCurrent = [], guards = [], onChange = [], onRedirect, onNotFound, onBlock, }) {
10
10
  this.started = false;
11
11
  this.trailingSlash = false;
12
12
  this.strictTrailingSlash = true;
13
+ this.viewTransitionsEnabled = false;
13
14
  this.mergeSlashes = false;
14
15
  this.trailingSlash = trailingSlash !== null && trailingSlash !== void 0 ? trailingSlash : false;
15
16
  this.strictTrailingSlash = typeof trailingSlash === 'undefined';
16
17
  this.mergeSlashes = mergeSlashes !== null && mergeSlashes !== void 0 ? mergeSlashes : false;
18
+ this.viewTransitionsEnabled = enableViewTransitions !== null && enableViewTransitions !== void 0 ? enableViewTransitions : false;
17
19
  this.hooks = new Map([
18
20
  ['beforeResolve', new Set(beforeResolve)],
19
21
  ['beforeNavigate', new Set(beforeNavigate)],
@@ -108,7 +110,7 @@ class AbstractRouter {
108
110
  }
109
111
  async internalNavigate(navigateOptions, { history, redirect }) {
110
112
  var _a;
111
- const { url, replace, params, navigateState, code } = navigateOptions;
113
+ const { url, replace, params, navigateState, code, viewTransition } = navigateOptions;
112
114
  const prevNavigation = redirect
113
115
  ? this.lastNavigation
114
116
  : (_a = this.currentNavigation) !== null && _a !== void 0 ? _a : this.lastNavigation;
@@ -131,6 +133,9 @@ class AbstractRouter {
131
133
  redirectFrom,
132
134
  key: this.uuid(),
133
135
  };
136
+ if (this.viewTransitionsEnabled && viewTransition !== undefined) {
137
+ navigation.viewTransition = viewTransition;
138
+ }
134
139
  await this.runHooks('beforeResolve', navigation);
135
140
  const to = this.resolveRoute({ url: resolvedUrl, params, navigateState }, { wildcard: true });
136
141
  if (to) {
@@ -168,7 +173,7 @@ class AbstractRouter {
168
173
  }
169
174
  }
170
175
  resolve(resolveOptions, options) {
171
- const opts = typeof resolveOptions === 'string' ? { url: resolveOptions } : resolveOptions;
176
+ const opts = makeNavigateOptions(resolveOptions);
172
177
  return this.resolveRoute({ ...opts, url: parse(opts.url) }, options);
173
178
  }
174
179
  back(options) {
@@ -5,6 +5,7 @@ import type { RouteTree } from '../tree/tree';
5
5
  export interface Options {
6
6
  trailingSlash?: boolean;
7
7
  mergeSlashes?: boolean;
8
+ enableViewTransitions?: boolean;
8
9
  routes?: Route[];
9
10
  onRedirect?: NavigationHook;
10
11
  onNotFound?: NavigationHook;
@@ -26,6 +27,7 @@ export declare abstract class AbstractRouter {
26
27
  protected started: boolean;
27
28
  protected trailingSlash: boolean;
28
29
  protected strictTrailingSlash: boolean;
30
+ protected viewTransitionsEnabled: boolean;
29
31
  protected mergeSlashes: boolean;
30
32
  protected currentNavigation: Navigation;
31
33
  protected lastNavigation: Navigation;
@@ -35,7 +37,7 @@ export declare abstract class AbstractRouter {
35
37
  protected hooks: Map<HookName, Set<NavigationHook>>;
36
38
  protected syncHooks: Map<SyncHookName, Set<NavigationSyncHook>>;
37
39
  private currentUuid;
38
- constructor({ trailingSlash, mergeSlashes, beforeResolve, beforeNavigate, afterNavigate, beforeUpdateCurrent, afterUpdateCurrent, guards, onChange, onRedirect, onNotFound, onBlock, }: Options);
40
+ constructor({ trailingSlash, mergeSlashes, enableViewTransitions, beforeResolve, beforeNavigate, afterNavigate, beforeUpdateCurrent, afterUpdateCurrent, guards, onChange, onRedirect, onNotFound, onBlock, }: Options);
39
41
  protected onRedirect?: NavigationHook;
40
42
  protected onNotFound?: NavigationHook;
41
43
  protected onBlock?: NavigationHook;
@@ -6,14 +6,16 @@ import { logger } from '../logger.es.js';
6
6
  import { makeNavigateOptions, normalizeManySlashes, normalizeTrailingSlash, isSameHost, registerHook } from '../utils.es.js';
7
7
 
8
8
  class AbstractRouter {
9
- constructor({ trailingSlash, mergeSlashes, beforeResolve = [], beforeNavigate = [], afterNavigate = [], beforeUpdateCurrent = [], afterUpdateCurrent = [], guards = [], onChange = [], onRedirect, onNotFound, onBlock, }) {
9
+ constructor({ trailingSlash, mergeSlashes, enableViewTransitions, beforeResolve = [], beforeNavigate = [], afterNavigate = [], beforeUpdateCurrent = [], afterUpdateCurrent = [], guards = [], onChange = [], onRedirect, onNotFound, onBlock, }) {
10
10
  this.started = false;
11
11
  this.trailingSlash = false;
12
12
  this.strictTrailingSlash = true;
13
+ this.viewTransitionsEnabled = false;
13
14
  this.mergeSlashes = false;
14
15
  this.trailingSlash = trailingSlash !== null && trailingSlash !== void 0 ? trailingSlash : false;
15
16
  this.strictTrailingSlash = typeof trailingSlash === 'undefined';
16
17
  this.mergeSlashes = mergeSlashes !== null && mergeSlashes !== void 0 ? mergeSlashes : false;
18
+ this.viewTransitionsEnabled = enableViewTransitions !== null && enableViewTransitions !== void 0 ? enableViewTransitions : false;
17
19
  this.hooks = new Map([
18
20
  ['beforeResolve', new Set(beforeResolve)],
19
21
  ['beforeNavigate', new Set(beforeNavigate)],
@@ -108,7 +110,7 @@ class AbstractRouter {
108
110
  }
109
111
  async internalNavigate(navigateOptions, { history, redirect }) {
110
112
  var _a;
111
- const { url, replace, params, navigateState, code } = navigateOptions;
113
+ const { url, replace, params, navigateState, code, viewTransition } = navigateOptions;
112
114
  const prevNavigation = redirect
113
115
  ? this.lastNavigation
114
116
  : (_a = this.currentNavigation) !== null && _a !== void 0 ? _a : this.lastNavigation;
@@ -131,6 +133,9 @@ class AbstractRouter {
131
133
  redirectFrom,
132
134
  key: this.uuid(),
133
135
  };
136
+ if (this.viewTransitionsEnabled && viewTransition !== undefined) {
137
+ navigation.viewTransition = viewTransition;
138
+ }
134
139
  await this.runHooks('beforeResolve', navigation);
135
140
  const to = this.resolveRoute({ url: resolvedUrl, params, navigateState }, { wildcard: true });
136
141
  if (to) {
@@ -168,7 +173,7 @@ class AbstractRouter {
168
173
  }
169
174
  }
170
175
  resolve(resolveOptions, options) {
171
- const opts = typeof resolveOptions === 'string' ? { url: resolveOptions } : resolveOptions;
176
+ const opts = makeNavigateOptions(resolveOptions);
172
177
  return this.resolveRoute({ ...opts, url: parse(opts.url) }, options);
173
178
  }
174
179
  back(options) {
@@ -15,14 +15,16 @@ var isString__default = /*#__PURE__*/_interopDefaultLegacy(isString);
15
15
  var isObject__default = /*#__PURE__*/_interopDefaultLegacy(isObject);
16
16
 
17
17
  class AbstractRouter {
18
- constructor({ trailingSlash, mergeSlashes, beforeResolve = [], beforeNavigate = [], afterNavigate = [], beforeUpdateCurrent = [], afterUpdateCurrent = [], guards = [], onChange = [], onRedirect, onNotFound, onBlock, }) {
18
+ constructor({ trailingSlash, mergeSlashes, enableViewTransitions, beforeResolve = [], beforeNavigate = [], afterNavigate = [], beforeUpdateCurrent = [], afterUpdateCurrent = [], guards = [], onChange = [], onRedirect, onNotFound, onBlock, }) {
19
19
  this.started = false;
20
20
  this.trailingSlash = false;
21
21
  this.strictTrailingSlash = true;
22
+ this.viewTransitionsEnabled = false;
22
23
  this.mergeSlashes = false;
23
24
  this.trailingSlash = trailingSlash !== null && trailingSlash !== void 0 ? trailingSlash : false;
24
25
  this.strictTrailingSlash = typeof trailingSlash === 'undefined';
25
26
  this.mergeSlashes = mergeSlashes !== null && mergeSlashes !== void 0 ? mergeSlashes : false;
27
+ this.viewTransitionsEnabled = enableViewTransitions !== null && enableViewTransitions !== void 0 ? enableViewTransitions : false;
26
28
  this.hooks = new Map([
27
29
  ['beforeResolve', new Set(beforeResolve)],
28
30
  ['beforeNavigate', new Set(beforeNavigate)],
@@ -117,7 +119,7 @@ class AbstractRouter {
117
119
  }
118
120
  async internalNavigate(navigateOptions, { history, redirect }) {
119
121
  var _a;
120
- const { url, replace, params, navigateState, code } = navigateOptions;
122
+ const { url, replace, params, navigateState, code, viewTransition } = navigateOptions;
121
123
  const prevNavigation = redirect
122
124
  ? this.lastNavigation
123
125
  : (_a = this.currentNavigation) !== null && _a !== void 0 ? _a : this.lastNavigation;
@@ -140,6 +142,9 @@ class AbstractRouter {
140
142
  redirectFrom,
141
143
  key: this.uuid(),
142
144
  };
145
+ if (this.viewTransitionsEnabled && viewTransition !== undefined) {
146
+ navigation.viewTransition = viewTransition;
147
+ }
143
148
  await this.runHooks('beforeResolve', navigation);
144
149
  const to = this.resolveRoute({ url: resolvedUrl, params, navigateState }, { wildcard: true });
145
150
  if (to) {
@@ -177,7 +182,7 @@ class AbstractRouter {
177
182
  }
178
183
  }
179
184
  resolve(resolveOptions, options) {
180
- const opts = typeof resolveOptions === 'string' ? { url: resolveOptions } : resolveOptions;
185
+ const opts = utils.makeNavigateOptions(resolveOptions);
181
186
  return this.resolveRoute({ ...opts, url: url.parse(opts.url) }, options);
182
187
  }
183
188
  back(options) {
@@ -3,11 +3,18 @@ import { logger } from '../logger.browser.js';
3
3
  import { RouteTree } from '../tree/tree.browser.js';
4
4
 
5
5
  const DELAY_CHECK_INTERVAL = 50;
6
+ const APPLIED_VIEW_TRANSITIONS_KEY = '_t_view_transitions';
6
7
  class Router extends ClientRouter {
7
8
  constructor(options) {
8
9
  super(options);
9
10
  this.tree = new RouteTree(options.routes);
10
11
  this.history.setTree(this.tree);
12
+ if (this.viewTransitionsEnabled) {
13
+ this.restoreAppliedViewTransitions();
14
+ window.addEventListener('pagehide', () => {
15
+ this.saveAppliedViewTransitions();
16
+ });
17
+ }
11
18
  }
12
19
  async rehydrate(navigation) {
13
20
  return this.resolveIfDelayFound(super.rehydrate(navigation));
@@ -20,7 +27,11 @@ class Router extends ClientRouter {
20
27
  return this.flattenDelayedNavigation(delayedNavigation);
21
28
  }
22
29
  }
23
- async run(navigation) {
30
+ async run(payload) {
31
+ const navigation = { ...payload };
32
+ if (this.viewTransitionsEnabled) {
33
+ navigation.viewTransition = this.shouldApplyViewTransition(payload);
34
+ }
24
35
  // if router is not started yet delay current navigation without blocking promise resolving
25
36
  if (!this.started) {
26
37
  this.delayNavigation(navigation);
@@ -161,6 +172,60 @@ class Router extends ClientRouter {
161
172
  this.delayedReject = null;
162
173
  });
163
174
  }
175
+ shouldApplyViewTransition(navigation) {
176
+ var _a, _b;
177
+ const from = navigation.from.actualPath;
178
+ const to = navigation.to.redirect !== undefined
179
+ ? (_a = this.resolve(navigation.to.redirect)) === null || _a === void 0 ? void 0 : _a.actualPath
180
+ : (_b = navigation.to) === null || _b === void 0 ? void 0 : _b.actualPath;
181
+ // If View Transition enabled to current navigation, save it
182
+ if (navigation.viewTransition) {
183
+ const toPaths = this.appliedViewTransitions.get(from);
184
+ if (this.appliedViewTransitions.get(from)) {
185
+ toPaths.add(to);
186
+ }
187
+ else {
188
+ this.appliedViewTransitions.set(from, new Set([to]));
189
+ }
190
+ return true;
191
+ }
192
+ const priorPaths = this.appliedViewTransitions.get(from);
193
+ // If we were on the route with view transition previously,
194
+ // then there must be back navigation with view transition also
195
+ if (priorPaths !== undefined && priorPaths.has(to)) {
196
+ return true;
197
+ }
198
+ // If we don't have a previous forward navigation, assume we are
199
+ // going back from the new route and enable view transition if it
200
+ // was previously enabled
201
+ return this.appliedViewTransitions.has(to);
202
+ }
203
+ restoreAppliedViewTransitions() {
204
+ try {
205
+ const valueFromStorage = sessionStorage.getItem(APPLIED_VIEW_TRANSITIONS_KEY);
206
+ if (valueFromStorage !== null) {
207
+ const parsedValue = JSON.parse(valueFromStorage);
208
+ this.appliedViewTransitions = new Map(Object.entries(parsedValue).map(([key, value]) => [key, new Set(value)]));
209
+ return;
210
+ }
211
+ this.appliedViewTransitions = new Map();
212
+ }
213
+ catch (error) {
214
+ this.appliedViewTransitions = new Map();
215
+ }
216
+ }
217
+ saveAppliedViewTransitions() {
218
+ if (this.appliedViewTransitions.size > 0) {
219
+ try {
220
+ const valueToSave = {};
221
+ for (const [key, value] of this.appliedViewTransitions) {
222
+ valueToSave[key] = [...value];
223
+ }
224
+ sessionStorage.setItem(APPLIED_VIEW_TRANSITIONS_KEY, JSON.stringify(valueToSave));
225
+ }
226
+ catch (error) { }
227
+ }
228
+ }
164
229
  }
165
230
 
166
231
  export { Router };
@@ -6,15 +6,19 @@ export declare class Router extends ClientRouter {
6
6
  protected delayedPromise: Promise<void>;
7
7
  protected delayedResolve: () => void;
8
8
  protected delayedReject: (error: Error) => void;
9
+ private appliedViewTransitions;
9
10
  constructor(options: Options);
10
11
  rehydrate(navigation: Navigation): Promise<void>;
11
12
  start(): Promise<any>;
12
- protected run(navigation: Navigation): Promise<any>;
13
+ protected run(payload: Navigation): Promise<any>;
13
14
  protected delayNavigation(navigation: Navigation): Promise<void>;
14
15
  protected commitNavigation(navigation: Navigation): void;
15
16
  protected runGuards(navigation: Navigation): Promise<void>;
16
17
  protected runHooks(hookName: HookName, navigation: Navigation): Promise<void>;
17
18
  private resolveIfDelayFound;
18
19
  private flattenDelayedNavigation;
20
+ private shouldApplyViewTransition;
21
+ private restoreAppliedViewTransitions;
22
+ private saveAppliedViewTransitions;
19
23
  }
20
24
  //# sourceMappingURL=browser.d.ts.map
package/lib/types.d.ts CHANGED
@@ -26,6 +26,7 @@ export interface BaseNavigateOptions {
26
26
  }
27
27
  export interface NavigateOptions extends BaseNavigateOptions {
28
28
  url?: string;
29
+ viewTransition?: boolean;
29
30
  }
30
31
  export type UpdateCurrentRouteOptions = BaseNavigateOptions;
31
32
  export interface HistoryOptions {
@@ -50,6 +51,10 @@ export interface Navigation {
50
51
  * Defines internally within the router
51
52
  */
52
53
  key?: number;
54
+ /**
55
+ * Activates View Transition to this navigation
56
+ */
57
+ viewTransition?: boolean;
53
58
  }
54
59
  export type NavigationGuard = (navigation: Navigation) => Promise<void | NavigateOptions | string | boolean>;
55
60
  export type NavigationHook = (navigation: Navigation) => Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinkoff/router",
3
- "version": "0.3.1",
3
+ "version": "0.3.8",
4
4
  "description": "router",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
@@ -27,6 +27,7 @@
27
27
  "use-sync-external-store": "^1.2.0"
28
28
  },
29
29
  "peerDependencies": {
30
+ "@tramvai/core": "3.9.0",
30
31
  "react": ">=16.14.0",
31
32
  "tslib": "^2.4.0"
32
33
  },
@@ -1,16 +0,0 @@
1
- import { jsx } from 'react/jsx-runtime';
2
- import { useMemo } from 'react';
3
- import { useSyncExternalStore } from 'use-sync-external-store/shim';
4
- import { RouterContext, RouteContext, UrlContext } from './context.browser.js';
5
-
6
- const Provider = ({ router, serverState, children }) => {
7
- // fallback to outdate router implementation
8
- const lastRoute = useMemo(() => router.getCurrentRoute(), [router]);
9
- const lastUrl = useMemo(() => router.getCurrentUrl(), [router]);
10
- const route = useSyncExternalStore((cb) => router.registerSyncHook('change', cb), () => { var _a, _b; return (_b = (_a = router.getLastRoute) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastRoute; }, serverState ? () => serverState.currentRoute : () => { var _a, _b; return (_b = (_a = router.getLastRoute) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastRoute; });
11
- const url = useSyncExternalStore((cb) => router.registerSyncHook('change', cb), () => { var _a, _b; return (_b = (_a = router.getLastUrl) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastUrl; }, serverState ? () => serverState.currentUrl : () => { var _a, _b; return (_b = (_a = router.getLastUrl) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastUrl; });
12
- return (jsx(RouterContext.Provider, { value: router, children: jsx(RouteContext.Provider, { value: route, children: jsx(UrlContext.Provider, { value: url, children: children }) }) }));
13
- };
14
- Provider.displayName = 'TinkoffRouterProvider';
15
-
16
- export { Provider };
@@ -1,13 +0,0 @@
1
- import type { FunctionComponent, ReactNode } from 'react';
2
- import type { Url } from '@tinkoff/url';
3
- import type { AbstractRouter } from '../../router/abstract';
4
- import type { NavigationRoute } from '../../types';
5
- export declare const Provider: FunctionComponent<{
6
- router: AbstractRouter;
7
- serverState?: {
8
- currentRoute: NavigationRoute;
9
- currentUrl: Url;
10
- };
11
- children?: ReactNode;
12
- }>;
13
- //# sourceMappingURL=provider.d.ts.map
@@ -1,16 +0,0 @@
1
- import { jsx } from 'react/jsx-runtime';
2
- import { useMemo } from 'react';
3
- import { useSyncExternalStore } from 'use-sync-external-store/shim';
4
- import { RouterContext, RouteContext, UrlContext } from './context.es.js';
5
-
6
- const Provider = ({ router, serverState, children }) => {
7
- // fallback to outdate router implementation
8
- const lastRoute = useMemo(() => router.getCurrentRoute(), [router]);
9
- const lastUrl = useMemo(() => router.getCurrentUrl(), [router]);
10
- const route = useSyncExternalStore((cb) => router.registerSyncHook('change', cb), () => { var _a, _b; return (_b = (_a = router.getLastRoute) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastRoute; }, serverState ? () => serverState.currentRoute : () => { var _a, _b; return (_b = (_a = router.getLastRoute) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastRoute; });
11
- const url = useSyncExternalStore((cb) => router.registerSyncHook('change', cb), () => { var _a, _b; return (_b = (_a = router.getLastUrl) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastUrl; }, serverState ? () => serverState.currentUrl : () => { var _a, _b; return (_b = (_a = router.getLastUrl) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastUrl; });
12
- return (jsx(RouterContext.Provider, { value: router, children: jsx(RouteContext.Provider, { value: route, children: jsx(UrlContext.Provider, { value: url, children: children }) }) }));
13
- };
14
- Provider.displayName = 'TinkoffRouterProvider';
15
-
16
- export { Provider };
@@ -1,20 +0,0 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- var jsxRuntime = require('react/jsx-runtime');
6
- var react = require('react');
7
- var shim = require('use-sync-external-store/shim');
8
- var context = require('./context.js');
9
-
10
- const Provider = ({ router, serverState, children }) => {
11
- // fallback to outdate router implementation
12
- const lastRoute = react.useMemo(() => router.getCurrentRoute(), [router]);
13
- const lastUrl = react.useMemo(() => router.getCurrentUrl(), [router]);
14
- const route = shim.useSyncExternalStore((cb) => router.registerSyncHook('change', cb), () => { var _a, _b; return (_b = (_a = router.getLastRoute) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastRoute; }, serverState ? () => serverState.currentRoute : () => { var _a, _b; return (_b = (_a = router.getLastRoute) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastRoute; });
15
- const url = shim.useSyncExternalStore((cb) => router.registerSyncHook('change', cb), () => { var _a, _b; return (_b = (_a = router.getLastUrl) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastUrl; }, serverState ? () => serverState.currentUrl : () => { var _a, _b; return (_b = (_a = router.getLastUrl) === null || _a === void 0 ? void 0 : _a.call(router)) !== null && _b !== void 0 ? _b : lastUrl; });
16
- return (jsxRuntime.jsx(context.RouterContext.Provider, { value: router, children: jsxRuntime.jsx(context.RouteContext.Provider, { value: route, children: jsxRuntime.jsx(context.UrlContext.Provider, { value: url, children: children }) }) }));
17
- };
18
- Provider.displayName = 'TinkoffRouterProvider';
19
-
20
- exports.Provider = Provider;