@tinkoff/router 0.7.67 → 0.7.70

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.
@@ -18,8 +18,8 @@ const useViewTransition = (payload) => {
18
18
  // We are handling forward and back navigations for the same route here
19
19
  isTransitioning: [currentPath, nextPath].includes(path),
20
20
  types: viewTransition.types,
21
- isForward: viewTransition.types.includes(NAVIGATION_TYPE.FORWARD),
22
- isBack: viewTransition.types.includes(NAVIGATION_TYPE.BACK),
21
+ isForward: viewTransition.types?.includes(NAVIGATION_TYPE.FORWARD),
22
+ isBack: viewTransition.types?.includes(NAVIGATION_TYPE.BACK),
23
23
  };
24
24
  }
25
25
  return { isTransitioning: false, isForward: false, isBack: false };
@@ -18,8 +18,8 @@ const useViewTransition = (payload) => {
18
18
  // We are handling forward and back navigations for the same route here
19
19
  isTransitioning: [currentPath, nextPath].includes(path),
20
20
  types: viewTransition.types,
21
- isForward: viewTransition.types.includes(NAVIGATION_TYPE.FORWARD),
22
- isBack: viewTransition.types.includes(NAVIGATION_TYPE.BACK),
21
+ isForward: viewTransition.types?.includes(NAVIGATION_TYPE.FORWARD),
22
+ isBack: viewTransition.types?.includes(NAVIGATION_TYPE.BACK),
23
23
  };
24
24
  }
25
25
  return { isTransitioning: false, isForward: false, isBack: false };
@@ -22,8 +22,8 @@ const useViewTransition = (payload) => {
22
22
  // We are handling forward and back navigations for the same route here
23
23
  isTransitioning: [currentPath, nextPath].includes(path),
24
24
  types: viewTransition.types,
25
- isForward: viewTransition.types.includes(constants.NAVIGATION_TYPE.FORWARD),
26
- isBack: viewTransition.types.includes(constants.NAVIGATION_TYPE.BACK),
25
+ isForward: viewTransition.types?.includes(constants.NAVIGATION_TYPE.FORWARD),
26
+ isBack: viewTransition.types?.includes(constants.NAVIGATION_TYPE.BACK),
27
27
  };
28
28
  }
29
29
  return { isTransitioning: false, isForward: false, isBack: false };
@@ -96,7 +96,7 @@ class ClientHistory extends History {
96
96
  this.currentState = state;
97
97
  navigateState = state.navigateState;
98
98
  if (key !== prevKey) {
99
- navigateState = 'navigate';
99
+ navigationType = 'navigate';
100
100
  }
101
101
  else {
102
102
  if (this.backNavigationWithinRouteType === BackNavigationType.PREFER_UPDATE) {
@@ -96,7 +96,7 @@ class ClientHistory extends History {
96
96
  this.currentState = state;
97
97
  navigateState = state.navigateState;
98
98
  if (key !== prevKey) {
99
- navigateState = 'navigate';
99
+ navigationType = 'navigate';
100
100
  }
101
101
  else {
102
102
  if (this.backNavigationWithinRouteType === BackNavigationType.PREFER_UPDATE) {
@@ -100,7 +100,7 @@ class ClientHistory extends base.History {
100
100
  this.currentState = state;
101
101
  navigateState = state.navigateState;
102
102
  if (key !== prevKey) {
103
- navigateState = 'navigate';
103
+ navigationType = 'navigate';
104
104
  }
105
105
  else {
106
106
  if (this.backNavigationWithinRouteType === types.BackNavigationType.PREFER_UPDATE) {
@@ -102,6 +102,7 @@ class AbstractRouter {
102
102
  this.internalHooks = {
103
103
  'router:resolve-route': this.hooksFactory.createSync('router:resolve-route'),
104
104
  'router:resolve-url': this.hooksFactory.createSync('router:resolve-url'),
105
+ 'router:resolve-view-transition': this.hooksFactory.createSync('router:resolve-view-transition'),
105
106
  };
106
107
  this.internalHooks['router:resolve-route'].tap('router', (_, args) => {
107
108
  return this._resolveRoute(...args);
@@ -109,6 +110,9 @@ class AbstractRouter {
109
110
  this.internalHooks['router:resolve-url'].tap('router', (_, navigation) => {
110
111
  return this._resolveUrl(navigation);
111
112
  });
113
+ this.internalHooks['router:resolve-view-transition'].tap('router', (_, params) => {
114
+ return this._resolveViewTransition(params);
115
+ });
112
116
  this.plugins.forEach((plugin) => {
113
117
  plugin.apply(this);
114
118
  });
@@ -133,6 +137,19 @@ class AbstractRouter {
133
137
  // same as getCurrentRoute
134
138
  return this.currentNavigation?.url ?? this.lastNavigation?.url;
135
139
  }
140
+ getCurrentViewTransition() {
141
+ const currentNavigation = this.currentNavigation ?? this.lastNavigation;
142
+ if (!currentNavigation || !('viewTransition' in currentNavigation)) {
143
+ return null;
144
+ }
145
+ const viewTransition = {
146
+ viewTransition: currentNavigation.viewTransition,
147
+ };
148
+ if ('viewTransitionTypes' in currentNavigation) {
149
+ viewTransition.viewTransitionTypes = currentNavigation.viewTransitionTypes;
150
+ }
151
+ return viewTransition;
152
+ }
136
153
  getLastRoute() {
137
154
  return this.lastNavigation?.to;
138
155
  }
@@ -477,6 +494,9 @@ class AbstractRouter {
477
494
  navigateState,
478
495
  };
479
496
  }
497
+ _resolveViewTransition(params) {
498
+ return null;
499
+ }
480
500
  }
481
501
 
482
502
  export { AbstractRouter };
@@ -51,6 +51,18 @@ export declare abstract class AbstractRouter {
51
51
  readonly internalHooks: {
52
52
  'router:resolve-route': SyncTapableHookInstance<Parameters<AbstractRouter['_resolveRoute']>, NavigationRoute | undefined>;
53
53
  'router:resolve-url': SyncTapableHookInstance<NavigateOptions, Url>;
54
+ 'router:resolve-view-transition': SyncTapableHookInstance<{
55
+ navigation: Navigation;
56
+ from: string;
57
+ to: string;
58
+ previousViewTransition: {
59
+ viewTransition: boolean;
60
+ viewTransitionTypes?: string[];
61
+ };
62
+ }, {
63
+ viewTransition?: boolean;
64
+ viewTransitionTypes?: string[];
65
+ } | null>;
54
66
  };
55
67
  readonly navigateHook: AsyncTapableHookInstance<{
56
68
  navigateOptions: NavigateOptions | string;
@@ -86,6 +98,10 @@ export declare abstract class AbstractRouter {
86
98
  start(): Promise<void>;
87
99
  getCurrentRoute(): NavigationRoute;
88
100
  getCurrentUrl(): Url;
101
+ getCurrentViewTransition(): {
102
+ viewTransition: boolean;
103
+ viewTransitionTypes?: string[];
104
+ };
89
105
  getLastRoute(): NavigationRoute;
90
106
  getLastUrl(): Url;
91
107
  protected commitNavigation(navigation: Navigation): void;
@@ -127,6 +143,15 @@ export declare abstract class AbstractRouter {
127
143
  }, { wildcard }?: {
128
144
  wildcard?: boolean;
129
145
  }): NavigationRoute;
146
+ protected _resolveViewTransition(params: {
147
+ navigation: Navigation;
148
+ from: string;
149
+ to: string;
150
+ previousViewTransition: {
151
+ viewTransition: boolean;
152
+ viewTransitionTypes?: string[];
153
+ };
154
+ }): any;
130
155
  }
131
156
  export {};
132
157
  //# sourceMappingURL=abstract.d.ts.map
@@ -102,6 +102,7 @@ class AbstractRouter {
102
102
  this.internalHooks = {
103
103
  'router:resolve-route': this.hooksFactory.createSync('router:resolve-route'),
104
104
  'router:resolve-url': this.hooksFactory.createSync('router:resolve-url'),
105
+ 'router:resolve-view-transition': this.hooksFactory.createSync('router:resolve-view-transition'),
105
106
  };
106
107
  this.internalHooks['router:resolve-route'].tap('router', (_, args) => {
107
108
  return this._resolveRoute(...args);
@@ -109,6 +110,9 @@ class AbstractRouter {
109
110
  this.internalHooks['router:resolve-url'].tap('router', (_, navigation) => {
110
111
  return this._resolveUrl(navigation);
111
112
  });
113
+ this.internalHooks['router:resolve-view-transition'].tap('router', (_, params) => {
114
+ return this._resolveViewTransition(params);
115
+ });
112
116
  this.plugins.forEach((plugin) => {
113
117
  plugin.apply(this);
114
118
  });
@@ -133,6 +137,19 @@ class AbstractRouter {
133
137
  // same as getCurrentRoute
134
138
  return this.currentNavigation?.url ?? this.lastNavigation?.url;
135
139
  }
140
+ getCurrentViewTransition() {
141
+ const currentNavigation = this.currentNavigation ?? this.lastNavigation;
142
+ if (!currentNavigation || !('viewTransition' in currentNavigation)) {
143
+ return null;
144
+ }
145
+ const viewTransition = {
146
+ viewTransition: currentNavigation.viewTransition,
147
+ };
148
+ if ('viewTransitionTypes' in currentNavigation) {
149
+ viewTransition.viewTransitionTypes = currentNavigation.viewTransitionTypes;
150
+ }
151
+ return viewTransition;
152
+ }
136
153
  getLastRoute() {
137
154
  return this.lastNavigation?.to;
138
155
  }
@@ -477,6 +494,9 @@ class AbstractRouter {
477
494
  navigateState,
478
495
  };
479
496
  }
497
+ _resolveViewTransition(params) {
498
+ return null;
499
+ }
480
500
  }
481
501
 
482
502
  export { AbstractRouter };
@@ -111,6 +111,7 @@ class AbstractRouter {
111
111
  this.internalHooks = {
112
112
  'router:resolve-route': this.hooksFactory.createSync('router:resolve-route'),
113
113
  'router:resolve-url': this.hooksFactory.createSync('router:resolve-url'),
114
+ 'router:resolve-view-transition': this.hooksFactory.createSync('router:resolve-view-transition'),
114
115
  };
115
116
  this.internalHooks['router:resolve-route'].tap('router', (_, args) => {
116
117
  return this._resolveRoute(...args);
@@ -118,6 +119,9 @@ class AbstractRouter {
118
119
  this.internalHooks['router:resolve-url'].tap('router', (_, navigation) => {
119
120
  return this._resolveUrl(navigation);
120
121
  });
122
+ this.internalHooks['router:resolve-view-transition'].tap('router', (_, params) => {
123
+ return this._resolveViewTransition(params);
124
+ });
121
125
  this.plugins.forEach((plugin) => {
122
126
  plugin.apply(this);
123
127
  });
@@ -142,6 +146,19 @@ class AbstractRouter {
142
146
  // same as getCurrentRoute
143
147
  return this.currentNavigation?.url ?? this.lastNavigation?.url;
144
148
  }
149
+ getCurrentViewTransition() {
150
+ const currentNavigation = this.currentNavigation ?? this.lastNavigation;
151
+ if (!currentNavigation || !('viewTransition' in currentNavigation)) {
152
+ return null;
153
+ }
154
+ const viewTransition = {
155
+ viewTransition: currentNavigation.viewTransition,
156
+ };
157
+ if ('viewTransitionTypes' in currentNavigation) {
158
+ viewTransition.viewTransitionTypes = currentNavigation.viewTransitionTypes;
159
+ }
160
+ return viewTransition;
161
+ }
145
162
  getLastRoute() {
146
163
  return this.lastNavigation?.to;
147
164
  }
@@ -486,6 +503,9 @@ class AbstractRouter {
486
503
  navigateState,
487
504
  };
488
505
  }
506
+ _resolveViewTransition(params) {
507
+ return null;
508
+ }
489
509
  }
490
510
 
491
511
  exports.AbstractRouter = AbstractRouter;
@@ -4,7 +4,7 @@ import { RouteTree } from '../tree/tree.browser.js';
4
4
  import { NAVIGATION_TYPE } from './constants.browser.js';
5
5
 
6
6
  const DELAY_CHECK_INTERVAL = 50;
7
- const APPLIED_VIEW_TRANSITIONS_KEY = '_t_view_transitions';
7
+ const APPLIED_VIEW_TRANSITIONS_KEY = '_t_view_transitions_v2';
8
8
  class Router extends ClientRouter {
9
9
  delayedNavigation;
10
10
  delayedPromise;
@@ -34,6 +34,7 @@ class Router extends ClientRouter {
34
34
  return this.flattenDelayedNavigation(delayedNavigation);
35
35
  }
36
36
  }
37
+ // eslint-disable-next-line max-statements
37
38
  async run(navigation) {
38
39
  // if navigation was cancelled before, do not run it
39
40
  if (navigation.skipped) {
@@ -47,17 +48,46 @@ class Router extends ClientRouter {
47
48
  ? this.resolve(navigation.to.redirect)?.actualPath
48
49
  : navigation.to?.actualPath) ?? '';
49
50
  const from = navigation.from?.actualPath ?? '';
50
- if (this.viewTransitionsEnabled && to !== from && !navigation.hasUAVisualTransition) {
51
- navigation.viewTransition = this.shouldApplyViewTransition(navigation, to);
52
- const hasNavigationType = !!navigation.viewTransitionTypes &&
53
- navigation.viewTransitionTypes.some((type) => Object.values(NAVIGATION_TYPE).includes(type));
54
- // add a navigation type only if it has not been provided
55
- if (navigation.viewTransition && !hasNavigationType) {
56
- const navigationType = this.getNavigationType(navigation);
57
- // add a navigation type to transition types
58
- navigation.viewTransitionTypes = navigation.viewTransitionTypes
59
- ? [...(navigation.viewTransitionTypes ?? []), navigationType]
60
- : [navigationType];
51
+ if (this.viewTransitionsEnabled) {
52
+ const prevTransitionByIndex = this.getPrevTransition(navigation, to);
53
+ const previousViewTransition = prevTransitionByIndex
54
+ ? {
55
+ viewTransition: true,
56
+ viewTransitionTypes: prevTransitionByIndex && prevTransitionByIndex?.viewTransitionTypes,
57
+ }
58
+ : {
59
+ viewTransition: false,
60
+ };
61
+ const viewTransition = this.internalHooks['router:resolve-view-transition'].call({
62
+ navigation,
63
+ from,
64
+ to,
65
+ previousViewTransition,
66
+ });
67
+ logger.debug({
68
+ event: 'view-transition.resolved',
69
+ navigation,
70
+ viewTransition,
71
+ previousViewTransition,
72
+ });
73
+ if (viewTransition) {
74
+ navigation.viewTransition = viewTransition.viewTransition;
75
+ navigation.viewTransitionTypes = viewTransition.viewTransitionTypes;
76
+ }
77
+ const historyState = this.history.getCurrentState();
78
+ // sync applied view transitions
79
+ if (!navigation.history && historyState) {
80
+ if (navigation.viewTransition) {
81
+ // add transition except history navigations with VT enabled
82
+ this.appliedViewTransitions.set(historyState.index, {
83
+ path: from,
84
+ viewTransitionTypes: navigation.viewTransitionTypes,
85
+ });
86
+ // if we have forward navigation without VT enabled and stored val for this index
87
+ }
88
+ else if (this.appliedViewTransitions.has(historyState.index)) {
89
+ this.appliedViewTransitions.delete(historyState.index);
90
+ }
61
91
  }
62
92
  }
63
93
  // if router is not started yet delay current navigation without blocking promise resolving
@@ -191,41 +221,74 @@ class Router extends ClientRouter {
191
221
  return NAVIGATION_TYPE.FORWARD;
192
222
  }
193
223
  shouldApplyViewTransition(navigation, to) {
194
- const from = navigation.from?.actualPath ?? '';
195
224
  const historyState = this.history.getCurrentState();
196
225
  if (!historyState) {
226
+ logger.debug({
227
+ event: 'view-transition.not-applied',
228
+ message: 'View Transition not applied because there is no history state, probably it is the first navigation in app',
229
+ navigation,
230
+ });
197
231
  return false;
198
232
  }
199
- const historyIndex = historyState.index;
200
- const prevTransitionFrom = this.getPrevTransition(navigation);
233
+ const prevTransitionByIndex = this.getPrevTransition(navigation, to);
201
234
  // handle back navigation when prev navigation was with VT enabled
202
- if (navigation.history && prevTransitionFrom === to) {
235
+ if (navigation.history && navigation.isBack && !!prevTransitionByIndex) {
236
+ logger.debug({
237
+ event: 'view-transition.should-apply',
238
+ message: 'View Transition should be applied for browser back navigation with VT enabled on previous navigation',
239
+ navigation,
240
+ });
203
241
  return true;
204
242
  }
205
243
  if (navigation.viewTransition) {
206
- // add transition except history navigations with VT enabled
207
- !navigation.history && this.appliedViewTransitions.set(historyIndex, from);
244
+ logger.debug({
245
+ event: 'view-transition.should-apply',
246
+ message: 'View Transition should be applied because it is present on navigation parameters',
247
+ navigation,
248
+ });
208
249
  return true;
209
250
  }
210
251
  // handle forward navigation
211
252
  if (navigation.history && !navigation.isBack) {
212
253
  // returns true if prev navigation was with VT enabled
213
- return !!prevTransitionFrom;
214
- }
215
- // if we have forward navigation without VT enabled and stored val for this index
216
- if (!navigation.history && this.appliedViewTransitions.has(historyIndex)) {
217
- this.appliedViewTransitions.delete(historyIndex);
254
+ const result = !!prevTransitionByIndex;
255
+ if (result) {
256
+ logger.debug({
257
+ event: 'view-transition.should-apply',
258
+ message: 'View Transition should be applied for browser forward navigation because previous navigation was with VT enabled',
259
+ navigation,
260
+ });
261
+ }
262
+ else {
263
+ logger.debug({
264
+ event: 'view-transition.not-applied',
265
+ message: 'View Transition not applied for browser forward navigation because previous navigation was without VT',
266
+ navigation,
267
+ });
268
+ }
269
+ return result;
218
270
  }
271
+ logger.debug({
272
+ event: 'view-transition.not-applied',
273
+ navigation,
274
+ });
219
275
  return false;
220
276
  }
221
- getPrevTransition(navigation) {
277
+ getPrevTransition(navigation, target) {
222
278
  const historyState = this.history.getCurrentState();
223
279
  if (!historyState) {
224
280
  return false;
225
281
  }
282
+ const isBrowserBackNavigation = navigation.history && navigation.isBack;
226
283
  const historyIndex = historyState.index;
227
- const index = navigation.history && navigation.isBack ? historyIndex : historyIndex - 1;
228
- return this.appliedViewTransitions.get(index);
284
+ const index = isBrowserBackNavigation ? historyIndex : historyIndex - 1;
285
+ const result = this.appliedViewTransitions.get(index);
286
+ if (result) {
287
+ // can't be sure, but this check looks like replace state protection,
288
+ // when we save a different route on the same index
289
+ return result && result.path === target ? result : false;
290
+ }
291
+ return false;
229
292
  }
230
293
  restoreAppliedViewTransitions() {
231
294
  try {
@@ -287,6 +350,40 @@ class Router extends ClientRouter {
287
350
  return this._flatten(delayedNavigation);
288
351
  }
289
352
  }
353
+ _resolveViewTransition({ navigation, from, to, }) {
354
+ if (!this.viewTransitionsEnabled) {
355
+ return null;
356
+ }
357
+ if (to === from) {
358
+ logger.debug({
359
+ event: 'view-transition.disabled',
360
+ message: 'View Transition is disabled for same route navigation',
361
+ navigation,
362
+ });
363
+ return null;
364
+ }
365
+ if (navigation.hasUAVisualTransition) {
366
+ logger.debug({
367
+ event: 'view-transition.disabled',
368
+ message: 'View Transition is disabled because another View Transition is already running',
369
+ navigation,
370
+ });
371
+ return null;
372
+ }
373
+ const result = {};
374
+ result.viewTransition = this.shouldApplyViewTransition(navigation, to);
375
+ const hasNavigationType = !!navigation.viewTransitionTypes &&
376
+ navigation.viewTransitionTypes.some((type) => Object.values(NAVIGATION_TYPE).includes(type));
377
+ // add a navigation type only if it has not been provided
378
+ if (result.viewTransition && !hasNavigationType) {
379
+ const navigationType = this.getNavigationType(navigation);
380
+ // add a navigation type to transition types
381
+ result.viewTransitionTypes = navigation.viewTransitionTypes
382
+ ? [...(navigation.viewTransitionTypes ?? []), navigationType]
383
+ : [navigationType];
384
+ }
385
+ return result;
386
+ }
290
387
  }
291
388
 
292
389
  export { Router };
@@ -24,5 +24,13 @@ export declare class Router extends ClientRouter {
24
24
  private saveAppliedViewTransitions;
25
25
  cancel(navigation?: Navigation): void | Navigation;
26
26
  private _flatten;
27
+ _resolveViewTransition({ navigation, from, to, }: {
28
+ navigation: Navigation;
29
+ from: string;
30
+ to: string;
31
+ }): {
32
+ viewTransition?: boolean;
33
+ viewTransitionTypes?: string[];
34
+ };
27
35
  }
28
36
  //# sourceMappingURL=browser.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinkoff/router",
3
- "version": "0.7.67",
3
+ "version": "0.7.70",
4
4
  "description": "router",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
@@ -28,7 +28,7 @@
28
28
  "use-sync-external-store": "^1.4.0"
29
29
  },
30
30
  "peerDependencies": {
31
- "@tramvai/core": "7.19.1",
31
+ "@tramvai/core": "7.20.2",
32
32
  "react": ">=16.14.0",
33
33
  "tslib": "^2.4.0"
34
34
  },