@vkontakte/vkui 7.11.5 → 7.11.7

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 (57) hide show
  1. package/dist/components/OnboardingTooltip/OnboardingTooltip.d.ts.map +1 -1
  2. package/dist/components/OnboardingTooltip/OnboardingTooltip.js +3 -1
  3. package/dist/components/OnboardingTooltip/OnboardingTooltip.js.map +1 -1
  4. package/dist/components/Popper/Popper.d.ts.map +1 -1
  5. package/dist/components/Popper/Popper.js +3 -1
  6. package/dist/components/Popper/Popper.js.map +1 -1
  7. package/dist/components/Root/Root.d.ts.map +1 -1
  8. package/dist/components/Root/Root.js +38 -31
  9. package/dist/components/Root/Root.js.map +1 -1
  10. package/dist/components/View/View.d.ts.map +1 -1
  11. package/dist/components/View/View.js +59 -52
  12. package/dist/components/View/View.js.map +1 -1
  13. package/dist/components/View/ViewInfinite.d.ts.map +1 -1
  14. package/dist/components/View/ViewInfinite.js +43 -36
  15. package/dist/components/View/ViewInfinite.js.map +1 -1
  16. package/dist/components.css +1 -1
  17. package/dist/components.css.map +1 -1
  18. package/dist/cssm/components/CustomScrollView/CustomScrollView.module.css +29 -23
  19. package/dist/cssm/components/OnboardingTooltip/OnboardingTooltip.js +3 -1
  20. package/dist/cssm/components/OnboardingTooltip/OnboardingTooltip.js.map +1 -1
  21. package/dist/cssm/components/Popper/Popper.js +3 -1
  22. package/dist/cssm/components/Popper/Popper.js.map +1 -1
  23. package/dist/cssm/components/Root/Root.js +36 -29
  24. package/dist/cssm/components/Root/Root.js.map +1 -1
  25. package/dist/cssm/components/View/View.js +57 -50
  26. package/dist/cssm/components/View/View.js.map +1 -1
  27. package/dist/cssm/components/View/ViewInfinite.js +41 -34
  28. package/dist/cssm/components/View/ViewInfinite.js.map +1 -1
  29. package/dist/cssm/lib/floating/LockFloatingPosition/LockFloatingPosition.js +6 -0
  30. package/dist/cssm/lib/floating/LockFloatingPosition/LockFloatingPosition.js.map +1 -0
  31. package/dist/cssm/lib/floating/useFloatingWithInteractions/useFloatingWithInteractions.js +3 -1
  32. package/dist/cssm/lib/floating/useFloatingWithInteractions/useFloatingWithInteractions.js.map +1 -1
  33. package/dist/cssm/lib/sheet/controllers/BottomSheetController.js +11 -2
  34. package/dist/cssm/lib/sheet/controllers/BottomSheetController.js.map +1 -1
  35. package/dist/lib/floating/LockFloatingPosition/LockFloatingPosition.d.ts +6 -0
  36. package/dist/lib/floating/LockFloatingPosition/LockFloatingPosition.d.ts.map +1 -0
  37. package/dist/lib/floating/LockFloatingPosition/LockFloatingPosition.js +6 -0
  38. package/dist/lib/floating/LockFloatingPosition/LockFloatingPosition.js.map +1 -0
  39. package/dist/lib/floating/useFloatingWithInteractions/useFloatingWithInteractions.d.ts.map +1 -1
  40. package/dist/lib/floating/useFloatingWithInteractions/useFloatingWithInteractions.js +3 -1
  41. package/dist/lib/floating/useFloatingWithInteractions/useFloatingWithInteractions.js.map +1 -1
  42. package/dist/lib/sheet/controllers/BottomSheetController.d.ts.map +1 -1
  43. package/dist/lib/sheet/controllers/BottomSheetController.js +11 -2
  44. package/dist/lib/sheet/controllers/BottomSheetController.js.map +1 -1
  45. package/dist/vkui.css +1 -1
  46. package/dist/vkui.css.map +1 -1
  47. package/package.json +1 -1
  48. package/src/components/CustomScrollView/CustomScrollView.module.css +29 -23
  49. package/src/components/CustomScrollView/CustomScrollView.module.css.d.ts.map +1 -1
  50. package/src/components/OnboardingTooltip/OnboardingTooltip.tsx +6 -1
  51. package/src/components/Popper/Popper.tsx +12 -7
  52. package/src/components/Root/Root.tsx +55 -48
  53. package/src/components/View/View.tsx +94 -83
  54. package/src/components/View/ViewInfinite.tsx +74 -65
  55. package/src/lib/floating/LockFloatingPosition/LockFloatingPosition.tsx +6 -0
  56. package/src/lib/floating/useFloatingWithInteractions/useFloatingWithInteractions.ts +4 -1
  57. package/src/lib/sheet/controllers/BottomSheetController.ts +12 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "type": "module",
3
- "version": "7.11.5",
3
+ "version": "7.11.7",
4
4
  "name": "@vkontakte/vkui",
5
5
  "description": "VKUI library",
6
6
  "module": "./dist/index.js",
@@ -20,33 +20,39 @@
20
20
  }
21
21
  }
22
22
  @supports not (scrollbar-color: auto) {
23
- .scrollbarHidden::-webkit-scrollbar {
24
- display: none;
25
- }
23
+ /**
24
+ * Android WebView не поддерживает scrollbar-color, поэтому отрисовываем
25
+ * полосы прокрутки только если основное указательное устройство мышь
26
+ */
27
+ @media (--pointer-has) {
28
+ .scrollbarHidden::-webkit-scrollbar {
29
+ display: none;
30
+ }
26
31
 
27
- .host::-webkit-scrollbar {
28
- inline-size: 12px;
29
- block-size: 12px;
30
- background: 0 0;
31
- }
32
+ .host::-webkit-scrollbar {
33
+ inline-size: 12px;
34
+ block-size: 12px;
35
+ background: 0 0;
36
+ }
32
37
 
33
- .host::-webkit-scrollbar-track,
34
- .host::-webkit-scrollbar-corner {
35
- background: transparent;
36
- }
38
+ .host::-webkit-scrollbar-track,
39
+ .host::-webkit-scrollbar-corner {
40
+ background: transparent;
41
+ }
37
42
 
38
- .host::-webkit-scrollbar-thumb,
39
- .host::-webkit-scrollbar-thumb {
40
- background-color: var(--vkui--color_icon_tertiary_alpha);
41
- background-clip: padding-box, content-box;
42
- border: 3px solid transparent;
43
- border-radius: 6px;
44
- }
43
+ .host::-webkit-scrollbar-thumb,
44
+ .host::-webkit-scrollbar-thumb {
45
+ background-color: var(--vkui--color_icon_tertiary_alpha);
46
+ background-clip: padding-box, content-box;
47
+ border: 3px solid transparent;
48
+ border-radius: 6px;
49
+ }
45
50
 
46
- .host::-webkit-scrollbar-thumb:hover,
47
- .host::-webkit-scrollbar-thumb:active {
48
- background-color: var(--vkui--color_icon_tertiary_alpha--hover);
49
- border: 2px solid transparent;
51
+ .host::-webkit-scrollbar-thumb:hover,
52
+ .host::-webkit-scrollbar-thumb:active {
53
+ background-color: var(--vkui--color_icon_tertiary_alpha--hover);
54
+ border: 2px solid transparent;
55
+ }
50
56
  }
51
57
  }
52
58
 
@@ -1 +1 @@
1
- {"version":3,"sources":["./CustomScrollView.module.css"],"names":["host","scrollbarHidden","overscrollBehaviorContain","overscrollBehaviorNone","scrollBehaviorSmooth","horizontalScrollEnabled"],"mappings":"AAAA;AAAA,E,aAAAA,M,WAAA;AAAA,E,aAQEA,M,WARF;AAAA,E,aAaEC,iB,WAbF;AAAA,E,aAsBEA,iB,WAtBF;AAAA,E,aA0BED,M,WA1BF;AAAA,E,aAgCEA,M,WAhCF;AAAA,E,aAiCIA,M,WAjCJ;AAAA,E,aAqCEA,M,WArCF;AAAA,E,aAsCIA,M,WAtCJ;AAAA,E,aA6CEA,M,WA7CF;AAAA,E,aA8CIA,M,WA9CJ;AAAA,E,aAoDAE,2B,WApDA;AAAA,E,aAwDAC,wB,WAxDA;AAAA,E,aA4DAC,sB,WA5DA;AAAA,E,aAgEAC,yB,WAhEA;AAAA;AAAA","file":"CustomScrollView.module.css.d.ts","sourceRoot":""}
1
+ {"version":3,"sources":["./CustomScrollView.module.css"],"names":["host","scrollbarHidden","overscrollBehaviorContain","overscrollBehaviorNone","scrollBehaviorSmooth","horizontalScrollEnabled"],"mappings":"AAAA;AAAA,E,aAAAA,M,WAAA;AAAA,E,aAQEA,M,WARF;AAAA,E,aAaEC,iB,WAbF;AAAA,E,aA2BIA,iB,WA3BJ;AAAA,E,aA+BID,M,WA/BJ;AAAA,E,aAqCIA,M,WArCJ;AAAA,E,aAsCQA,M,WAtCR;AAAA,E,aA0CIA,M,WA1CJ;AAAA,E,aA2CQA,M,WA3CR;AAAA,E,aAkDIA,M,WAlDJ;AAAA,E,aAmDQA,M,WAnDR;AAAA,E,aA0DAE,2B,WA1DA;AAAA,E,aA8DAC,wB,WA9DA;AAAA,E,aAkEAC,sB,WAlEA;AAAA,E,aAsEAC,yB,WAtEA;AAAA;AAAA","file":"CustomScrollView.module.css.d.ts","sourceRoot":""}
@@ -15,6 +15,7 @@ import {
15
15
  useFloatingMiddlewaresBootstrap,
16
16
  usePlacementChangeCallback,
17
17
  } from '../../lib/floating';
18
+ import { LockFloatingPositionContext } from '../../lib/floating/LockFloatingPosition/LockFloatingPosition';
18
19
  import { useIsomorphicLayoutEffect } from '../../lib/useIsomorphicLayoutEffect';
19
20
  import { warnOnce } from '../../lib/warnOnce';
20
21
  import { DEFAULT_ARROW_HEIGHT, DEFAULT_ARROW_PADDING } from '../FloatingArrow/DefaultIcon';
@@ -131,6 +132,9 @@ export const OnboardingTooltip = ({
131
132
  disableShiftMiddleware,
132
133
  overflowPadding,
133
134
  });
135
+
136
+ const isLock = React.useContext(LockFloatingPositionContext);
137
+
134
138
  const {
135
139
  x: floatingDataX,
136
140
  y: floatingDataY,
@@ -141,8 +145,9 @@ export const OnboardingTooltip = ({
141
145
  strategy: positionStrategy,
142
146
  placement: strictPlacement,
143
147
  middleware: middlewares,
144
- whileElementsMounted: autoUpdateFloatingElement,
148
+ whileElementsMounted: isLock ? undefined : autoUpdateFloatingElement,
145
149
  });
150
+
146
151
  const tooltipRef = useExternRef<HTMLDivElement>(getRootRef, refs.setFloating);
147
152
  const [childRef, child] = usePatchChildren(children, {
148
153
  'aria-describedby': shown ? tooltipId : undefined,
@@ -12,6 +12,7 @@ import {
12
12
  usePlacementChangeCallback,
13
13
  type VirtualElement,
14
14
  } from '../../lib/floating';
15
+ import { LockFloatingPositionContext } from '../../lib/floating/LockFloatingPosition/LockFloatingPosition';
15
16
  import { useReferenceHiddenChangeCallback } from '../../lib/floating/useReferenceHiddenChangeCallback';
16
17
  import { useIsomorphicLayoutEffect } from '../../lib/useIsomorphicLayoutEffect';
17
18
  import type { HTMLAttributesWithRootRef } from '../../types';
@@ -156,6 +157,8 @@ export const Popper = ({
156
157
  overflowPadding,
157
158
  });
158
159
 
160
+ const isLock = React.useContext(LockFloatingPositionContext);
161
+
159
162
  const {
160
163
  x: floatingDataX,
161
164
  y: floatingDataY,
@@ -167,13 +170,15 @@ export const Popper = ({
167
170
  placement: strictPlacement,
168
171
  strategy: strategyProp,
169
172
  middleware: middlewares,
170
- whileElementsMounted(...args) {
171
- /* istanbul ignore next: не знаю как проверить */
172
- return autoUpdateFloatingElement(...args, {
173
- elementResize: autoUpdateOnTargetResize,
174
- animationFrame: autoUpdateOnAnimationFrame,
175
- });
176
- },
173
+ whileElementsMounted: isLock
174
+ ? undefined
175
+ : (...args) => {
176
+ /* istanbul ignore next: не знаю как проверить */
177
+ return autoUpdateFloatingElement(...args, {
178
+ elementResize: autoUpdateOnTargetResize,
179
+ animationFrame: autoUpdateOnAnimationFrame,
180
+ });
181
+ },
177
182
  });
178
183
 
179
184
  usePlacementChangeCallback(placementProp, resolvedPlacement, onPlacementChange);
@@ -4,6 +4,7 @@ import * as React from 'react';
4
4
  import { classNames } from '@vkontakte/vkjs';
5
5
  import { usePlatform } from '../../hooks/usePlatform';
6
6
  import { useDOM } from '../../lib/dom';
7
+ import { LockFloatingPositionContext } from '../../lib/floating/LockFloatingPosition/LockFloatingPosition';
7
8
  import { getNavId, type NavIdProps } from '../../lib/getNavId';
8
9
  import { useIsomorphicLayoutEffect } from '../../lib/useIsomorphicLayoutEffect';
9
10
  import { warnOnce } from '../../lib/warnOnce';
@@ -114,57 +115,63 @@ export const Root = ({
114
115
  [transition, disableAnimation, finishTransition],
115
116
  );
116
117
 
117
- const onAnimationEnd = () => {
118
+ const onAnimationEnd: React.AnimationEventHandler<HTMLElement> = (e) => {
119
+ if (e.target !== e.currentTarget) {
120
+ return;
121
+ }
122
+
118
123
  finishTransition();
119
124
  };
120
125
 
121
126
  return (
122
- <RootComponent
123
- {...restProps}
124
- baseClassName={classNames(
125
- styles.host,
126
- platform === 'ios' && styles.ios,
127
- transition && styles.transition,
128
- )}
129
- >
130
- {views.map((view) => {
131
- const viewId = getNavId(view.props, warn);
132
- if (viewId !== activeView && !(transition && viewId === prevView)) {
133
- return null;
134
- }
135
- const isTransitionTarget = transition && viewId === (isBack ? prevView : activeView);
136
- const compensateScroll =
137
- transition && (viewId === prevView || (isBack && viewId === activeView));
138
- return (
139
- <div
140
- key={viewId}
141
- ref={(e) => {
142
- viewId && (viewNodes[viewId] = e);
143
- }}
144
- onAnimationEnd={isTransitionTarget ? onAnimationEnd : undefined}
145
- className={classNames(
146
- styles.view,
147
- transition && viewId === prevView && isBack && styles.viewHideBack,
148
- transition && viewId === prevView && !isBack && styles.viewHideForward,
149
- transition && viewId === activeView && isBack && styles.viewShowBack,
150
- transition && viewId === activeView && !isBack && styles.viewShowForward,
151
- )}
152
- >
153
- <NavTransitionDirectionProvider isBack={isBack}>
154
- <NavTransitionProvider entering={transition && viewId === activeView}>
155
- <div
156
- className={styles.scrollCompensation}
157
- style={{
158
- marginTop: compensateScroll ? viewId && -(scrolls[viewId] ?? 0) : undefined,
159
- }}
160
- >
161
- {view}
162
- </div>
163
- </NavTransitionProvider>
164
- </NavTransitionDirectionProvider>
165
- </div>
166
- );
167
- })}
168
- </RootComponent>
127
+ <LockFloatingPositionContext.Provider value={transition}>
128
+ <RootComponent
129
+ {...restProps}
130
+ baseClassName={classNames(
131
+ styles.host,
132
+ platform === 'ios' && styles.ios,
133
+ transition && styles.transition,
134
+ )}
135
+ >
136
+ {views.map((view) => {
137
+ const viewId = getNavId(view.props, warn);
138
+ if (viewId !== activeView && !(transition && viewId === prevView)) {
139
+ return null;
140
+ }
141
+ const isTransitionTarget = transition && viewId === (isBack ? prevView : activeView);
142
+ const compensateScroll =
143
+ transition && (viewId === prevView || (isBack && viewId === activeView));
144
+ return (
145
+ <div
146
+ key={viewId}
147
+ ref={(e) => {
148
+ viewId && (viewNodes[viewId] = e);
149
+ }}
150
+ onAnimationEnd={isTransitionTarget ? onAnimationEnd : undefined}
151
+ className={classNames(
152
+ styles.view,
153
+ transition && viewId === prevView && isBack && styles.viewHideBack,
154
+ transition && viewId === prevView && !isBack && styles.viewHideForward,
155
+ transition && viewId === activeView && isBack && styles.viewShowBack,
156
+ transition && viewId === activeView && !isBack && styles.viewShowForward,
157
+ )}
158
+ >
159
+ <NavTransitionDirectionProvider isBack={isBack}>
160
+ <NavTransitionProvider entering={transition && viewId === activeView}>
161
+ <div
162
+ className={styles.scrollCompensation}
163
+ style={{
164
+ marginTop: compensateScroll ? viewId && -(scrolls[viewId] ?? 0) : undefined,
165
+ }}
166
+ >
167
+ {view}
168
+ </div>
169
+ </NavTransitionProvider>
170
+ </NavTransitionDirectionProvider>
171
+ </div>
172
+ );
173
+ })}
174
+ </RootComponent>
175
+ </LockFloatingPositionContext.Provider>
169
176
  );
170
177
  };
@@ -6,6 +6,7 @@ import { usePlatform } from '../../hooks/usePlatform';
6
6
  import { usePrevious } from '../../hooks/usePrevious';
7
7
  import { millisecondsInSecond } from '../../lib/date';
8
8
  import { blurActiveElement, useDOM } from '../../lib/dom';
9
+ import { LockFloatingPositionContext } from '../../lib/floating/LockFloatingPosition/LockFloatingPosition';
9
10
  import { getNavId, type NavIdProps } from '../../lib/getNavId';
10
11
  import { warnOnce } from '../../lib/warnOnce';
11
12
  import type { HTMLAttributesWithRootRef } from '../../types';
@@ -170,7 +171,11 @@ export const View = ({
170
171
  [activePanelProp, layoutEffectCall, onTransition, scroll],
171
172
  );
172
173
 
173
- const handleAnimatedTargetAnimationEnd = () => {
174
+ const handleAnimatedTargetAnimationEnd: React.AnimationEventHandler<HTMLDivElement> = (e) => {
175
+ if (e.target !== e.currentTarget) {
176
+ return;
177
+ }
178
+
174
179
  if (prevPanel !== null) {
175
180
  flushTransition(prevPanel, Boolean(isBack));
176
181
  }
@@ -452,90 +457,96 @@ export const View = ({
452
457
 
453
458
  return (
454
459
  <NavViewIdContext.Provider value={id}>
455
- <Touch
456
- Component="section"
457
- {...restProps}
458
- className={classNames(
459
- styles.host,
460
- platform === 'ios' && classNames(styles.ios, 'vkuiInternalView--ios'),
461
- !disableAnimation && animated && styles.animated,
462
- !disableAnimation && swipingBack && styles.swipingBack,
463
- disableAnimation && styles.noMotion,
464
- className,
465
- )}
466
- onMoveX={
467
- iOSSwipeBackSimulationEnabled
468
- ? handleTouchMoveXForIOSSwipeBackSimulation
469
- : platform === 'ios'
470
- ? handleTouchMoveXForNativeIOSSwipeBackOrSwipeNext
471
- : undefined
472
- }
473
- onEnd={iOSSwipeBackSimulationEnabled ? handleTouchEndForIOSSwipeBackSimulation : undefined}
474
- >
475
- <div className={styles.panels}>
476
- {panels.map((panel) => {
477
- const panelId = getNavId(panel.props, warn);
478
-
479
- const isPanelActive = panelId === activePanel;
480
- const isPanelPrev = panelId === prevPanel;
481
- const isPanelNext = panelId === nextPanel;
482
- const isAnimatedTarget = animated && (isBack ? isPanelPrev : isPanelNext);
483
-
484
- const isSwipeBackPrev = panelId === swipeBackPrevPanel;
485
- const isSwipeBackNext = panelId === swipeBackNextPanel;
486
- const isSwipeBackTarget = swipeBackResult && isSwipeBackPrev;
487
-
488
- let scrollCompensateStyle: React.CSSProperties | undefined = undefined;
489
-
490
- if (isPanelPrev || (isPanelNext && isBack) || isSwipeBackPrev || isSwipeBackNext) {
491
- const marginTop = scrolls.current[panelId];
492
- if (marginTop !== undefined) {
493
- scrollCompensateStyle = { marginTop: -1 * marginTop };
460
+ <LockFloatingPositionContext.Provider value={swipingBack || animated}>
461
+ <Touch
462
+ Component="section"
463
+ {...restProps}
464
+ className={classNames(
465
+ styles.host,
466
+ platform === 'ios' && classNames(styles.ios, 'vkuiInternalView--ios'),
467
+ !disableAnimation && animated && styles.animated,
468
+ !disableAnimation && swipingBack && styles.swipingBack,
469
+ disableAnimation && styles.noMotion,
470
+ className,
471
+ )}
472
+ onMoveX={
473
+ iOSSwipeBackSimulationEnabled
474
+ ? handleTouchMoveXForIOSSwipeBackSimulation
475
+ : platform === 'ios'
476
+ ? handleTouchMoveXForNativeIOSSwipeBackOrSwipeNext
477
+ : undefined
478
+ }
479
+ onEnd={
480
+ iOSSwipeBackSimulationEnabled ? handleTouchEndForIOSSwipeBackSimulation : undefined
481
+ }
482
+ >
483
+ <div className={styles.panels}>
484
+ {panels.map((panel) => {
485
+ const panelId = getNavId(panel.props, warn);
486
+
487
+ const isPanelActive = panelId === activePanel;
488
+ const isPanelPrev = panelId === prevPanel;
489
+ const isPanelNext = panelId === nextPanel;
490
+ const isAnimatedTarget = animated && (isBack ? isPanelPrev : isPanelNext);
491
+
492
+ const isSwipeBackPrev = panelId === swipeBackPrevPanel;
493
+ const isSwipeBackNext = panelId === swipeBackNextPanel;
494
+ const isSwipeBackTarget = swipeBackResult && isSwipeBackPrev;
495
+
496
+ let scrollCompensateStyle: React.CSSProperties | undefined = undefined;
497
+
498
+ if (isPanelPrev || (isPanelNext && isBack) || isSwipeBackPrev || isSwipeBackNext) {
499
+ const marginTop = scrolls.current[panelId];
500
+ if (marginTop !== undefined) {
501
+ scrollCompensateStyle = { marginTop: -1 * marginTop };
502
+ }
494
503
  }
495
- }
496
-
497
- return (
498
- <div
499
- className={classNames(
500
- styles.panel,
501
-
502
- isPanelActive && styles.panelActive,
503
- isPanelPrev && styles.panelPrev,
504
- isPanelNext && styles.panelNext,
505
-
506
- isSwipeBackPrev && styles.panelSwipeBackPrev,
507
- isSwipeBackNext && styles.panelSwipeBackNext,
508
- swipeBackResult === 'success' && styles.panelSwipeBackSuccess,
509
- swipeBackResult === 'fail' && styles.panelSwipeBackFailed,
510
- )}
511
- onTransitionEnd={isSwipeBackTarget ? handleSwipeBackTargetTransitionEnd : undefined}
512
- onAnimationEnd={isAnimatedTarget ? handleAnimatedTargetAnimationEnd : undefined}
513
- ref={(el) => {
514
- panelId !== undefined && (panelNodes.current[panelId] = el);
515
- }}
516
- style={calcPanelSwipeStyles(isSwipeBackPrev, isSwipeBackNext)}
517
- key={panelId}
518
- >
519
- {platform === 'ios' && (
520
- <div
521
- className={styles.panelOverlay}
522
- style={calcPanelSwipeBackOverlayStyles(isSwipeBackNext)}
523
- />
524
- )}
525
- <div className={styles.panelIn} style={scrollCompensateStyle}>
526
- <NavTransitionDirectionProvider isBack={swipingBack || isBack}>
527
- <NavTransitionProvider
528
- entering={panelId === nextPanel || panelId === swipeBackNextPanel}
529
- >
530
- {panel}
531
- </NavTransitionProvider>
532
- </NavTransitionDirectionProvider>
504
+
505
+ return (
506
+ <div
507
+ className={classNames(
508
+ styles.panel,
509
+
510
+ isPanelActive && styles.panelActive,
511
+ isPanelPrev && styles.panelPrev,
512
+ isPanelNext && styles.panelNext,
513
+
514
+ isSwipeBackPrev && styles.panelSwipeBackPrev,
515
+ isSwipeBackNext && styles.panelSwipeBackNext,
516
+ swipeBackResult === 'success' && styles.panelSwipeBackSuccess,
517
+ swipeBackResult === 'fail' && styles.panelSwipeBackFailed,
518
+ )}
519
+ onTransitionEnd={
520
+ isSwipeBackTarget ? handleSwipeBackTargetTransitionEnd : undefined
521
+ }
522
+ onAnimationEnd={isAnimatedTarget ? handleAnimatedTargetAnimationEnd : undefined}
523
+ ref={(el) => {
524
+ panelId !== undefined && (panelNodes.current[panelId] = el);
525
+ }}
526
+ style={calcPanelSwipeStyles(isSwipeBackPrev, isSwipeBackNext)}
527
+ key={panelId}
528
+ >
529
+ {platform === 'ios' && (
530
+ <div
531
+ className={styles.panelOverlay}
532
+ style={calcPanelSwipeBackOverlayStyles(isSwipeBackNext)}
533
+ />
534
+ )}
535
+ <div className={styles.panelIn} style={scrollCompensateStyle}>
536
+ <NavTransitionDirectionProvider isBack={swipingBack || isBack}>
537
+ <NavTransitionProvider
538
+ entering={panelId === nextPanel || panelId === swipeBackNextPanel}
539
+ >
540
+ {panel}
541
+ </NavTransitionProvider>
542
+ </NavTransitionDirectionProvider>
543
+ </div>
533
544
  </div>
534
- </div>
535
- );
536
- })}
537
- </div>
538
- </Touch>
545
+ );
546
+ })}
547
+ </div>
548
+ </Touch>
549
+ </LockFloatingPositionContext.Provider>
539
550
  </NavViewIdContext.Provider>
540
551
  );
541
552
  };
@@ -6,6 +6,7 @@ import { withContext } from '../../hoc/withContext';
6
6
  import { withPlatform } from '../../hoc/withPlatform';
7
7
  import { millisecondsInSecond } from '../../lib/date';
8
8
  import { canUseDOM, type DOMProps, withDOM } from '../../lib/dom';
9
+ import { LockFloatingPositionContext } from '../../lib/floating/LockFloatingPosition/LockFloatingPosition';
9
10
  import { getNavId, type NavIdProps } from '../../lib/getNavId';
10
11
  import { warnOnce } from '../../lib/warnOnce';
11
12
  import type { HasPlatform, HTMLAttributesWithRootRef } from '../../types';
@@ -380,7 +381,11 @@ class ViewInfiniteComponent extends React.Component<
380
381
  );
381
382
  }
382
383
 
383
- transitionEndHandler = (): void => {
384
+ transitionEndHandler: React.AnimationEventHandler<HTMLDivElement> = (e): void => {
385
+ if (e.target !== e.currentTarget) {
386
+ return;
387
+ }
388
+
384
389
  if (this.state.prevPanel !== null) {
385
390
  this.flushTransition(this.state.prevPanel, Boolean(this.state.isBack));
386
391
  }
@@ -617,74 +622,78 @@ class ViewInfiniteComponent extends React.Component<
617
622
 
618
623
  return (
619
624
  <NavViewIdContext.Provider value={id || nav}>
620
- <Touch
621
- Component="section"
622
- {...restProps}
623
- className={classNames(
624
- styles.host,
625
- platform === 'ios' && classNames(styles.ios, 'vkuiInternalView--ios'),
626
- !disableAnimation && this.state.animated && styles.animated,
627
- !disableAnimation && this.state.swipingBack && styles.swipingBack,
628
- disableAnimation && styles.noMotion,
629
- className,
630
- )}
631
- onMoveX={
632
- iOSSwipeBackSimulationEnabled
633
- ? this.handleTouchMoveXForIOSSwipeBackSimulation
634
- : platform === 'ios'
635
- ? this.handleTouchMoveXForNativeIOSSwipeBackOrSwipeNext
625
+ <LockFloatingPositionContext.Provider value={swipingBack || animated}>
626
+ <Touch
627
+ Component="section"
628
+ {...restProps}
629
+ className={classNames(
630
+ styles.host,
631
+ platform === 'ios' && classNames(styles.ios, 'vkuiInternalView--ios'),
632
+ !disableAnimation && this.state.animated && styles.animated,
633
+ !disableAnimation && this.state.swipingBack && styles.swipingBack,
634
+ disableAnimation && styles.noMotion,
635
+ className,
636
+ )}
637
+ onMoveX={
638
+ iOSSwipeBackSimulationEnabled
639
+ ? this.handleTouchMoveXForIOSSwipeBackSimulation
640
+ : platform === 'ios'
641
+ ? this.handleTouchMoveXForNativeIOSSwipeBackOrSwipeNext
642
+ : undefined
643
+ }
644
+ onEnd={
645
+ iOSSwipeBackSimulationEnabled
646
+ ? this.handleTouchEndForIOSSwipeBackSimulation
636
647
  : undefined
637
- }
638
- onEnd={
639
- iOSSwipeBackSimulationEnabled ? this.handleTouchEndForIOSSwipeBackSimulation : undefined
640
- }
641
- >
642
- <div className={styles.panels}>
643
- {panels.map((panel) => {
644
- const panelId = getNavId(panel.props, warn);
645
- const isPrev = panelId === prevPanel || panelId === swipeBackPrevPanel;
646
- const compensateScroll =
647
- isPrev || panelId === swipeBackNextPanel || (panelId === nextPanel && isBack);
648
- const isTransitionTarget = animated && panelId === (isBack ? prevPanel : nextPanel);
649
- const scrollList = (panelId && this.scrolls[panelId]) || [];
650
- const scroll = scrollList[scrollList.length - 1] || 0;
651
-
652
- return (
653
- <div
654
- className={classNames(
655
- styles.panel,
656
- panelId === activePanel && styles.panelActive,
657
- panelId === prevPanel && styles.panelPrev,
658
- panelId === nextPanel && styles.panelNext,
659
- panelId === swipeBackPrevPanel && styles.panelSwipeBackPrev,
660
- panelId === swipeBackNextPanel && styles.panelSwipeBackNext,
661
- swipeBackResult === 'success' && styles.panelSwipeBackSuccess,
662
- swipeBackResult === 'fail' && styles.panelSwipeBackFailed,
663
- )}
664
- onAnimationEnd={isTransitionTarget ? this.transitionEndHandler : undefined}
665
- ref={(el) => {
666
- panelId !== undefined && (this.panelNodes[panelId] = el);
667
- }}
668
- style={this.calcPanelSwipeStyles(panelId)}
669
- key={panelId}
670
- >
648
+ }
649
+ >
650
+ <div className={styles.panels}>
651
+ {panels.map((panel) => {
652
+ const panelId = getNavId(panel.props, warn);
653
+ const isPrev = panelId === prevPanel || panelId === swipeBackPrevPanel;
654
+ const compensateScroll =
655
+ isPrev || panelId === swipeBackNextPanel || (panelId === nextPanel && isBack);
656
+ const isTransitionTarget = animated && panelId === (isBack ? prevPanel : nextPanel);
657
+ const scrollList = (panelId && this.scrolls[panelId]) || [];
658
+ const scroll = scrollList[scrollList.length - 1] || 0;
659
+
660
+ return (
671
661
  <div
672
- className={styles.panelIn}
673
- style={{ marginTop: compensateScroll ? -scroll : undefined }}
662
+ className={classNames(
663
+ styles.panel,
664
+ panelId === activePanel && styles.panelActive,
665
+ panelId === prevPanel && styles.panelPrev,
666
+ panelId === nextPanel && styles.panelNext,
667
+ panelId === swipeBackPrevPanel && styles.panelSwipeBackPrev,
668
+ panelId === swipeBackNextPanel && styles.panelSwipeBackNext,
669
+ swipeBackResult === 'success' && styles.panelSwipeBackSuccess,
670
+ swipeBackResult === 'fail' && styles.panelSwipeBackFailed,
671
+ )}
672
+ onAnimationEnd={isTransitionTarget ? this.transitionEndHandler : undefined}
673
+ ref={(el) => {
674
+ panelId !== undefined && (this.panelNodes[panelId] = el);
675
+ }}
676
+ style={this.calcPanelSwipeStyles(panelId)}
677
+ key={panelId}
674
678
  >
675
- <NavTransitionDirectionProvider isBack={swipingBack || isBack}>
676
- <NavTransitionProvider
677
- entering={panelId === nextPanel || panelId === swipeBackNextPanel}
678
- >
679
- {panel}
680
- </NavTransitionProvider>
681
- </NavTransitionDirectionProvider>
679
+ <div
680
+ className={styles.panelIn}
681
+ style={{ marginTop: compensateScroll ? -scroll : undefined }}
682
+ >
683
+ <NavTransitionDirectionProvider isBack={swipingBack || isBack}>
684
+ <NavTransitionProvider
685
+ entering={panelId === nextPanel || panelId === swipeBackNextPanel}
686
+ >
687
+ {panel}
688
+ </NavTransitionProvider>
689
+ </NavTransitionDirectionProvider>
690
+ </div>
682
691
  </div>
683
- </div>
684
- );
685
- })}
686
- </div>
687
- </Touch>
692
+ );
693
+ })}
694
+ </div>
695
+ </Touch>
696
+ </LockFloatingPositionContext.Provider>
688
697
  </NavViewIdContext.Provider>
689
698
  );
690
699
  }
@@ -0,0 +1,6 @@
1
+ import * as React from 'react';
2
+
3
+ /**
4
+ * @see https://github.com/VKCOM/VKUI/pull/9603
5
+ */
6
+ export const LockFloatingPositionContext = React.createContext(false);