@vkontakte/vkui 7.6.1 → 7.6.2

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.
@@ -88,8 +88,7 @@
88
88
  padding-inline-start: var(--vkui--size_button_base_small_padding_horizontal--regular);
89
89
  }
90
90
 
91
- .sizeS .content:last-child,
92
- .sizeS .after {
91
+ .sizeS .content:last-child {
93
92
  -webkit-padding-end: var(--vkui--size_button_base_small_padding_horizontal--regular);
94
93
  padding-inline-end: var(--vkui--size_button_base_small_padding_horizontal--regular);
95
94
  }
@@ -99,8 +98,7 @@
99
98
  padding-inline-start: var(--vkui--size_button_base_medium_padding_horizontal--regular);
100
99
  }
101
100
 
102
- .sizeM .content:last-child,
103
- .sizeM .after {
101
+ .sizeM .content:last-child {
104
102
  -webkit-padding-end: var(--vkui--size_button_base_medium_padding_horizontal--regular);
105
103
  padding-inline-end: var(--vkui--size_button_base_medium_padding_horizontal--regular);
106
104
  }
@@ -110,8 +108,7 @@
110
108
  padding-inline-start: var(--vkui--size_button_base_large_padding_horizontal--regular);
111
109
  }
112
110
 
113
- .sizeL .content:last-child,
114
- .sizeL .after {
111
+ .sizeL .content:last-child {
115
112
  -webkit-padding-end: var(--vkui--size_button_base_large_padding_horizontal--regular);
116
113
  padding-inline-end: var(--vkui--size_button_base_large_padding_horizontal--regular);
117
114
  }
@@ -131,13 +128,27 @@
131
128
  padding-inline-start: var(--vkui--size_button_base_large_padding_horizontal_icon--regular);
132
129
  }
133
130
 
131
+ .sizeS .after {
132
+ -webkit-padding-end: var(--vkui--size_button_base_small_padding_horizontal_icon--regular);
133
+ padding-inline-end: var(--vkui--size_button_base_small_padding_horizontal_icon--regular);
134
+ }
135
+
136
+ .sizeM .after {
137
+ -webkit-padding-end: var(--vkui--size_button_base_medium_padding_horizontal_icon--regular);
138
+ padding-inline-end: var(--vkui--size_button_base_medium_padding_horizontal_icon--regular);
139
+ }
140
+
141
+ .sizeL .after {
142
+ -webkit-padding-end: var(--vkui--size_button_base_large_padding_horizontal_icon--regular);
143
+ padding-inline-end: var(--vkui--size_button_base_large_padding_horizontal_icon--regular);
144
+ }
145
+
134
146
  .modeTertiary.sizeS .content:first-child {
135
147
  -webkit-padding-start: var(--vkui--size_button_tertiary_small_padding_horizontal--regular);
136
148
  padding-inline-start: var(--vkui--size_button_tertiary_small_padding_horizontal--regular);
137
149
  }
138
150
 
139
- .modeTertiary.sizeS .content:last-child,
140
- .modeTertiary.sizeS .after {
151
+ .modeTertiary.sizeS .content:last-child {
141
152
  -webkit-padding-end: var(--vkui--size_button_tertiary_small_padding_horizontal--regular);
142
153
  padding-inline-end: var(--vkui--size_button_tertiary_small_padding_horizontal--regular);
143
154
  }
@@ -147,8 +158,7 @@
147
158
  padding-inline-start: var(--vkui--size_button_tertiary_medium_padding_horizontal--regular);
148
159
  }
149
160
 
150
- .modeTertiary.sizeM .content:last-child,
151
- .modeTertiary.sizeM .after {
161
+ .modeTertiary.sizeM .content:last-child {
152
162
  -webkit-padding-end: var(--vkui--size_button_tertiary_medium_padding_horizontal--regular);
153
163
  padding-inline-end: var(--vkui--size_button_tertiary_medium_padding_horizontal--regular);
154
164
  }
@@ -158,8 +168,7 @@
158
168
  padding-inline-start: var(--vkui--size_button_tertiary_large_padding_horizontal--regular);
159
169
  }
160
170
 
161
- .modeTertiary.sizeL .content:last-child,
162
- .modeTertiary.sizeL .after {
171
+ .modeTertiary.sizeL .content:last-child {
163
172
  -webkit-padding-end: var(--vkui--size_button_tertiary_large_padding_horizontal--regular);
164
173
  padding-inline-end: var(--vkui--size_button_tertiary_large_padding_horizontal--regular);
165
174
  }
@@ -179,6 +188,21 @@
179
188
  padding-inline-start: var(--vkui--size_button_tertiary_large_padding_horizontal_icon--regular);
180
189
  }
181
190
 
191
+ .modeTertiary.sizeS .after {
192
+ -webkit-padding-end: var(--vkui--size_button_tertiary_small_padding_horizontal_icon--regular);
193
+ padding-inline-end: var(--vkui--size_button_tertiary_small_padding_horizontal_icon--regular);
194
+ }
195
+
196
+ .modeTertiary.sizeM .after {
197
+ -webkit-padding-end: var(--vkui--size_button_tertiary_medium_padding_horizontal_icon--regular);
198
+ padding-inline-end: var(--vkui--size_button_tertiary_medium_padding_horizontal_icon--regular);
199
+ }
200
+
201
+ .modeTertiary.sizeL .after {
202
+ -webkit-padding-end: var(--vkui--size_button_tertiary_large_padding_horizontal_icon--regular);
203
+ padding-inline-end: var(--vkui--size_button_tertiary_large_padding_horizontal_icon--regular);
204
+ }
205
+
182
206
  .singleIcon .after,
183
207
  .singleIcon .before,
184
208
  .modeTertiary.singleIcon .after,
@@ -1,8 +1,10 @@
1
- import { useEffect, useRef } from "react";
1
+ import { useRef, useState } from "react";
2
2
  import { noop } from "@vkontakte/vkjs";
3
3
  import { useStableCallback } from "../../hooks/useStableCallback.js";
4
- import { useStateWithPrev } from "../../hooks/useStateWithPrev.js";
4
+ import { millisecondsInSecond } from "../date.js";
5
+ import { useIsomorphicLayoutEffect } from "../useIsomorphicLayoutEffect.js";
5
6
  /* istanbul ignore next: особенность рендера в браузере когда меняется className, в Jest не воспроизвести */ const forceReflowForFixNewMountedElement = (node)=>void node?.scrollTop;
7
+ const TRANSITION_FALLBACK_DELAY = 100;
6
8
  /**
7
9
  * Хук основан на компоненте `CSSTransition` из библиотеки `react-transition-group`.
8
10
  *
@@ -16,8 +18,9 @@ import { useStateWithPrev } from "../../hooks/useStateWithPrev.js";
16
18
  const onExit = useStableCallback(onExitProp || noop);
17
19
  const onExiting = useStableCallback(onExitingProp || noop);
18
20
  const onExited = useStableCallback(onExitedProp || noop);
21
+ const timerRef = useRef(null);
19
22
  const ref = useRef(null);
20
- const [[state, prevState], setState] = useStateWithPrev(()=>{
23
+ const [state, setState] = useState(()=>{
21
24
  if (!inProp) {
22
25
  return 'exited';
23
26
  }
@@ -27,7 +30,13 @@ import { useStateWithPrev } from "../../hooks/useStateWithPrev.js";
27
30
  }
28
31
  return 'entered';
29
32
  });
30
- useEffect(function updateState() {
33
+ const clearTimer = ()=>{
34
+ if (timerRef.current) {
35
+ clearTimeout(timerRef.current);
36
+ timerRef.current = null;
37
+ }
38
+ };
39
+ useIsomorphicLayoutEffect(function updateState() {
31
40
  if (inProp) {
32
41
  switch(state){
33
42
  case 'appear':
@@ -91,8 +100,6 @@ import { useStateWithPrev } from "../../hooks/useStateWithPrev.js";
91
100
  }, [
92
101
  inProp,
93
102
  state,
94
- prevState,
95
- setState,
96
103
  enableAppear,
97
104
  enableEnter,
98
105
  onEnter,
@@ -103,30 +110,52 @@ import { useStateWithPrev } from "../../hooks/useStateWithPrev.js";
103
110
  onExiting,
104
111
  onExited
105
112
  ]);
106
- const onTransitionEnd = (event)=>{
107
- /* istanbul ignore if: на всякий случай предупреждаем всплытие, нет смысла проверять условие */ if (event.target !== ref.current) {
108
- return;
109
- }
113
+ const completeTransition = useStableCallback((event)=>{
114
+ clearTimer();
110
115
  switch(state){
111
116
  case 'appearing':
112
117
  setState('appeared');
113
- onEntered(event.propertyName, true);
118
+ onEntered(event?.propertyName, true);
114
119
  break;
115
120
  case 'entering':
116
121
  setState('entered');
117
- onEntered(event.propertyName);
122
+ onEntered(event?.propertyName);
118
123
  break;
119
124
  case 'exiting':
120
125
  setState('exited');
121
- onExited(event.propertyName);
126
+ onExited(event?.propertyName);
122
127
  break;
123
128
  }
124
- };
129
+ });
130
+ useIsomorphicLayoutEffect(function scheduleTransitionCompletionFallback() {
131
+ const el = ref.current;
132
+ if (!el) {
133
+ return;
134
+ }
135
+ if (state === 'appearing' || state === 'entering' || state === 'exiting') {
136
+ const style = getComputedStyle(el);
137
+ const parseTime = (s)=>s.includes('ms') ? parseFloat(s) : parseFloat(s) * millisecondsInSecond;
138
+ const duration = Math.max(...style.transitionDuration.split(',').map(parseTime)) + Math.max(...style.transitionDelay.split(',').map(parseTime));
139
+ if (duration <= 0) {
140
+ completeTransition();
141
+ return;
142
+ }
143
+ // fallback если onTransitionEnd не пришёл
144
+ // TRANSITION_FALLBACK_DELAY, чтобы минимизировать вероятность,
145
+ // что setTimeout сработает раньше onTransitionEnd
146
+ timerRef.current = setTimeout(completeTransition, duration + TRANSITION_FALLBACK_DELAY);
147
+ return clearTimer;
148
+ }
149
+ return;
150
+ }, [
151
+ completeTransition,
152
+ state
153
+ ]);
125
154
  return [
126
155
  state,
127
156
  {
128
157
  ref,
129
- onTransitionEnd: state !== 'appeared' && state !== 'entered' && state !== 'exited' ? onTransitionEnd : undefined
158
+ onTransitionEnd: state !== 'appeared' && state !== 'entered' && state !== 'exited' ? completeTransition : undefined
130
159
  }
131
160
  ];
132
161
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/lib/animation/useCSSTransition.ts"],"sourcesContent":["import { type TransitionEvent, type TransitionEventHandler, useEffect, useRef } from 'react';\nimport { noop } from '@vkontakte/vkjs';\nimport { useStableCallback } from '../../hooks/useStableCallback';\nimport { useStateWithPrev } from '../../hooks/useStateWithPrev';\n\n/* istanbul ignore next: особенность рендера в браузере когда меняется className, в Jest не воспроизвести */\nconst forceReflowForFixNewMountedElement = (node: Element | null) => void node?.scrollTop;\n\nexport type UseCSSTransitionState =\n | 'appear'\n | 'appearing'\n | 'appeared'\n | 'enter'\n | 'entering'\n | 'entered'\n | 'exit'\n | 'exiting'\n | 'exited';\n\nexport type UseCSSTransitionOptions = {\n enableAppear?: boolean;\n enableEnter?: boolean;\n enableExit?: boolean;\n onEnter?: (appear?: boolean) => void;\n onEntering?: (appear?: boolean) => void;\n onEntered?: (propertyName?: string, appear?: boolean) => void;\n onExit?: () => void;\n onExiting?: () => void;\n onExited?: (propertyName?: string) => void;\n};\n\nexport type UseCSSTransition<Ref extends Element = Element> = [\n state: UseCSSTransitionState,\n {\n ref: React.RefObject<Ref | null>;\n onTransitionEnd?: TransitionEventHandler;\n },\n];\n\n/**\n * Хук основан на компоненте `CSSTransition` из библиотеки `react-transition-group`.\n *\n * @link https://reactcommunity.org/react-transition-group/css-transition\n *\n * @private\n */\nexport const useCSSTransition = <Ref extends Element = Element>(\n inProp?: boolean,\n {\n enableAppear = false,\n enableEnter = true,\n enableExit = true,\n onEnter: onEnterProp,\n onEntering: onEnteringProp,\n onEntered: onEnteredProp,\n onExit: onExitProp,\n onExiting: onExitingProp,\n onExited: onExitedProp,\n }: UseCSSTransitionOptions = {},\n): UseCSSTransition<Ref> => {\n const onEnter = useStableCallback(onEnterProp || noop);\n const onEntering = useStableCallback(onEnteringProp || noop);\n const onEntered = useStableCallback(onEnteredProp || noop);\n const onExit = useStableCallback(onExitProp || noop);\n const onExiting = useStableCallback(onExitingProp || noop);\n const onExited = useStableCallback(onExitedProp || noop);\n\n const ref = useRef<Ref | null>(null);\n const [[state, prevState], setState] = useStateWithPrev<UseCSSTransitionState>(() => {\n if (!inProp) {\n return 'exited';\n }\n\n if (enableAppear) {\n onEnter(true);\n return 'appear';\n }\n\n return 'entered';\n });\n\n useEffect(\n function updateState() {\n if (inProp) {\n switch (state) {\n case 'appear':\n forceReflowForFixNewMountedElement(ref.current);\n setState('appearing');\n onEntering(true);\n break;\n case 'enter':\n forceReflowForFixNewMountedElement(ref.current);\n setState('entering');\n onEntering();\n break;\n case 'exiting':\n if (enableEnter) {\n setState('entering');\n onEntering();\n break;\n }\n\n setState('entered');\n onEntered();\n break;\n case 'exited':\n if (enableEnter) {\n setState('enter');\n onEnter();\n break;\n }\n\n setState('entered');\n onEntered();\n break;\n }\n } else {\n switch (state) {\n case 'exit':\n forceReflowForFixNewMountedElement(ref.current);\n setState('exiting');\n onExiting();\n break;\n case 'appearing':\n case 'entering':\n if (enableExit) {\n setState('exiting');\n onExiting();\n break;\n }\n\n setState('exited');\n onExited();\n break;\n case 'appeared':\n case 'entered':\n if (enableExit) {\n setState('exit');\n onExit();\n break;\n }\n\n setState('exited');\n onExited();\n break;\n }\n }\n },\n [\n inProp,\n\n state,\n prevState,\n setState,\n\n enableAppear,\n enableEnter,\n onEnter,\n onEntering,\n onEntered,\n\n enableExit,\n onExit,\n onExiting,\n onExited,\n ],\n );\n\n const onTransitionEnd = (event: TransitionEvent) => {\n /* istanbul ignore if: на всякий случай предупреждаем всплытие, нет смысла проверять условие */\n if (event.target !== ref.current) {\n return;\n }\n\n switch (state) {\n case 'appearing':\n setState('appeared');\n onEntered(event.propertyName, true);\n break;\n case 'entering':\n setState('entered');\n onEntered(event.propertyName);\n break;\n case 'exiting':\n setState('exited');\n onExited(event.propertyName);\n break;\n }\n };\n\n return [\n state,\n {\n ref,\n onTransitionEnd:\n state !== 'appeared' && state !== 'entered' && state !== 'exited'\n ? onTransitionEnd\n : undefined,\n },\n ];\n};\n"],"names":["useEffect","useRef","noop","useStableCallback","useStateWithPrev","forceReflowForFixNewMountedElement","node","scrollTop","useCSSTransition","inProp","enableAppear","enableEnter","enableExit","onEnter","onEnterProp","onEntering","onEnteringProp","onEntered","onEnteredProp","onExit","onExitProp","onExiting","onExitingProp","onExited","onExitedProp","ref","state","prevState","setState","updateState","current","onTransitionEnd","event","target","propertyName","undefined"],"mappings":"AAAA,SAA4DA,SAAS,EAAEC,MAAM,QAAQ,QAAQ;AAC7F,SAASC,IAAI,QAAQ,kBAAkB;AACvC,SAASC,iBAAiB,QAAQ,mCAAgC;AAClE,SAASC,gBAAgB,QAAQ,kCAA+B;AAEhE,0GAA0G,GAC1G,MAAMC,qCAAqC,CAACC,OAAyB,KAAKA,MAAMC;AAiChF;;;;;;CAMC,GACD,OAAO,MAAMC,mBAAmB,CAC9BC,QACA,EACEC,eAAe,KAAK,EACpBC,cAAc,IAAI,EAClBC,aAAa,IAAI,EACjBC,SAASC,WAAW,EACpBC,YAAYC,cAAc,EAC1BC,WAAWC,aAAa,EACxBC,QAAQC,UAAU,EAClBC,WAAWC,aAAa,EACxBC,UAAUC,YAAY,EACE,GAAG,CAAC,CAAC;IAE/B,MAAMX,UAAUV,kBAAkBW,eAAeZ;IACjD,MAAMa,aAAaZ,kBAAkBa,kBAAkBd;IACvD,MAAMe,YAAYd,kBAAkBe,iBAAiBhB;IACrD,MAAMiB,SAAShB,kBAAkBiB,cAAclB;IAC/C,MAAMmB,YAAYlB,kBAAkBmB,iBAAiBpB;IACrD,MAAMqB,WAAWpB,kBAAkBqB,gBAAgBtB;IAEnD,MAAMuB,MAAMxB,OAAmB;IAC/B,MAAM,CAAC,CAACyB,OAAOC,UAAU,EAAEC,SAAS,GAAGxB,iBAAwC;QAC7E,IAAI,CAACK,QAAQ;YACX,OAAO;QACT;QAEA,IAAIC,cAAc;YAChBG,QAAQ;YACR,OAAO;QACT;QAEA,OAAO;IACT;IAEAb,UACE,SAAS6B;QACP,IAAIpB,QAAQ;YACV,OAAQiB;gBACN,KAAK;oBACHrB,mCAAmCoB,IAAIK,OAAO;oBAC9CF,SAAS;oBACTb,WAAW;oBACX;gBACF,KAAK;oBACHV,mCAAmCoB,IAAIK,OAAO;oBAC9CF,SAAS;oBACTb;oBACA;gBACF,KAAK;oBACH,IAAIJ,aAAa;wBACfiB,SAAS;wBACTb;wBACA;oBACF;oBAEAa,SAAS;oBACTX;oBACA;gBACF,KAAK;oBACH,IAAIN,aAAa;wBACfiB,SAAS;wBACTf;wBACA;oBACF;oBAEAe,SAAS;oBACTX;oBACA;YACJ;QACF,OAAO;YACL,OAAQS;gBACN,KAAK;oBACHrB,mCAAmCoB,IAAIK,OAAO;oBAC9CF,SAAS;oBACTP;oBACA;gBACF,KAAK;gBACL,KAAK;oBACH,IAAIT,YAAY;wBACdgB,SAAS;wBACTP;wBACA;oBACF;oBAEAO,SAAS;oBACTL;oBACA;gBACF,KAAK;gBACL,KAAK;oBACH,IAAIX,YAAY;wBACdgB,SAAS;wBACTT;wBACA;oBACF;oBAEAS,SAAS;oBACTL;oBACA;YACJ;QACF;IACF,GACA;QACEd;QAEAiB;QACAC;QACAC;QAEAlB;QACAC;QACAE;QACAE;QACAE;QAEAL;QACAO;QACAE;QACAE;KACD;IAGH,MAAMQ,kBAAkB,CAACC;QACvB,6FAA6F,GAC7F,IAAIA,MAAMC,MAAM,KAAKR,IAAIK,OAAO,EAAE;YAChC;QACF;QAEA,OAAQJ;YACN,KAAK;gBACHE,SAAS;gBACTX,UAAUe,MAAME,YAAY,EAAE;gBAC9B;YACF,KAAK;gBACHN,SAAS;gBACTX,UAAUe,MAAME,YAAY;gBAC5B;YACF,KAAK;gBACHN,SAAS;gBACTL,SAASS,MAAME,YAAY;gBAC3B;QACJ;IACF;IAEA,OAAO;QACLR;QACA;YACED;YACAM,iBACEL,UAAU,cAAcA,UAAU,aAAaA,UAAU,WACrDK,kBACAI;QACR;KACD;AACH,EAAE"}
1
+ {"version":3,"sources":["../../../../src/lib/animation/useCSSTransition.ts"],"sourcesContent":["import { type TransitionEvent, type TransitionEventHandler, useRef, useState } from 'react';\nimport { noop } from '@vkontakte/vkjs';\nimport { useStableCallback } from '../../hooks/useStableCallback';\nimport { millisecondsInSecond } from '../date';\nimport { useIsomorphicLayoutEffect } from '../useIsomorphicLayoutEffect';\n\n/* istanbul ignore next: особенность рендера в браузере когда меняется className, в Jest не воспроизвести */\nconst forceReflowForFixNewMountedElement = (node: Element | null) => void node?.scrollTop;\n\nexport type UseCSSTransitionState =\n | 'appear'\n | 'appearing'\n | 'appeared'\n | 'enter'\n | 'entering'\n | 'entered'\n | 'exit'\n | 'exiting'\n | 'exited';\n\nexport type UseCSSTransitionOptions = {\n enableAppear?: boolean;\n enableEnter?: boolean;\n enableExit?: boolean;\n onEnter?: (appear?: boolean) => void;\n onEntering?: (appear?: boolean) => void;\n onEntered?: (propertyName?: string, appear?: boolean) => void;\n onExit?: () => void;\n onExiting?: () => void;\n onExited?: (propertyName?: string) => void;\n};\n\nexport type UseCSSTransition<Ref extends Element = Element> = [\n state: UseCSSTransitionState,\n {\n ref: React.RefObject<Ref | null>;\n onTransitionEnd?: TransitionEventHandler;\n },\n];\n\nconst TRANSITION_FALLBACK_DELAY = 100;\n\n/**\n * Хук основан на компоненте `CSSTransition` из библиотеки `react-transition-group`.\n *\n * @link https://reactcommunity.org/react-transition-group/css-transition\n *\n * @private\n */\nexport const useCSSTransition = <Ref extends Element = Element>(\n inProp?: boolean,\n {\n enableAppear = false,\n enableEnter = true,\n enableExit = true,\n onEnter: onEnterProp,\n onEntering: onEnteringProp,\n onEntered: onEnteredProp,\n onExit: onExitProp,\n onExiting: onExitingProp,\n onExited: onExitedProp,\n }: UseCSSTransitionOptions = {},\n): UseCSSTransition<Ref> => {\n const onEnter = useStableCallback(onEnterProp || noop);\n const onEntering = useStableCallback(onEnteringProp || noop);\n const onEntered = useStableCallback(onEnteredProp || noop);\n const onExit = useStableCallback(onExitProp || noop);\n const onExiting = useStableCallback(onExitingProp || noop);\n const onExited = useStableCallback(onExitedProp || noop);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const ref = useRef<Ref | null>(null);\n const [state, setState] = useState<UseCSSTransitionState>(() => {\n if (!inProp) {\n return 'exited';\n }\n\n if (enableAppear) {\n onEnter(true);\n return 'appear';\n }\n\n return 'entered';\n });\n\n const clearTimer = () => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n };\n\n useIsomorphicLayoutEffect(\n function updateState() {\n if (inProp) {\n switch (state) {\n case 'appear':\n forceReflowForFixNewMountedElement(ref.current);\n setState('appearing');\n onEntering(true);\n break;\n case 'enter':\n forceReflowForFixNewMountedElement(ref.current);\n setState('entering');\n onEntering();\n break;\n case 'exiting':\n if (enableEnter) {\n setState('entering');\n onEntering();\n break;\n }\n\n setState('entered');\n onEntered();\n break;\n case 'exited':\n if (enableEnter) {\n setState('enter');\n onEnter();\n break;\n }\n\n setState('entered');\n onEntered();\n break;\n }\n } else {\n switch (state) {\n case 'exit':\n forceReflowForFixNewMountedElement(ref.current);\n setState('exiting');\n onExiting();\n break;\n case 'appearing':\n case 'entering':\n if (enableExit) {\n setState('exiting');\n onExiting();\n break;\n }\n\n setState('exited');\n onExited();\n break;\n case 'appeared':\n case 'entered':\n if (enableExit) {\n setState('exit');\n onExit();\n break;\n }\n\n setState('exited');\n onExited();\n break;\n }\n }\n },\n [\n inProp,\n\n state,\n enableAppear,\n enableEnter,\n onEnter,\n onEntering,\n onEntered,\n\n enableExit,\n onExit,\n onExiting,\n onExited,\n ],\n );\n\n const completeTransition = useStableCallback((event?: TransitionEvent) => {\n clearTimer();\n\n switch (state) {\n case 'appearing':\n setState('appeared');\n onEntered(event?.propertyName, true);\n break;\n case 'entering':\n setState('entered');\n onEntered(event?.propertyName);\n break;\n case 'exiting':\n setState('exited');\n onExited(event?.propertyName);\n break;\n }\n });\n\n useIsomorphicLayoutEffect(\n function scheduleTransitionCompletionFallback() {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n if (state === 'appearing' || state === 'entering' || state === 'exiting') {\n const style = getComputedStyle(el);\n\n const parseTime = (s: string) =>\n s.includes('ms') ? parseFloat(s) : parseFloat(s) * millisecondsInSecond;\n\n const duration =\n Math.max(...style.transitionDuration.split(',').map(parseTime)) +\n Math.max(...style.transitionDelay.split(',').map(parseTime));\n\n if (duration <= 0) {\n completeTransition();\n return;\n }\n\n // fallback если onTransitionEnd не пришёл\n // TRANSITION_FALLBACK_DELAY, чтобы минимизировать вероятность,\n // что setTimeout сработает раньше onTransitionEnd\n timerRef.current = setTimeout(completeTransition, duration + TRANSITION_FALLBACK_DELAY);\n\n return clearTimer;\n }\n return;\n },\n [completeTransition, state],\n );\n\n return [\n state,\n {\n ref,\n onTransitionEnd:\n state !== 'appeared' && state !== 'entered' && state !== 'exited'\n ? completeTransition\n : undefined,\n },\n ];\n};\n"],"names":["useRef","useState","noop","useStableCallback","millisecondsInSecond","useIsomorphicLayoutEffect","forceReflowForFixNewMountedElement","node","scrollTop","TRANSITION_FALLBACK_DELAY","useCSSTransition","inProp","enableAppear","enableEnter","enableExit","onEnter","onEnterProp","onEntering","onEnteringProp","onEntered","onEnteredProp","onExit","onExitProp","onExiting","onExitingProp","onExited","onExitedProp","timerRef","ref","state","setState","clearTimer","current","clearTimeout","updateState","completeTransition","event","propertyName","scheduleTransitionCompletionFallback","el","style","getComputedStyle","parseTime","s","includes","parseFloat","duration","Math","max","transitionDuration","split","map","transitionDelay","setTimeout","onTransitionEnd","undefined"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useCSSTransition.d.ts","sourceRoot":"","sources":["../../../src/lib/animation/useCSSTransition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,sBAAsB,EAAqB,MAAM,OAAO,CAAC;AAQ7F,MAAM,MAAM,qBAAqB,GAC7B,QAAQ,GACR,WAAW,GACX,UAAU,GACV,OAAO,GACP,UAAU,GACV,SAAS,GACT,MAAM,GACN,SAAS,GACT,QAAQ,CAAC;AAEb,MAAM,MAAM,uBAAuB,GAAG;IACpC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACrC,UAAU,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,SAAS,CAAC,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9D,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,QAAQ,CAAC,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAAC,GAAG,SAAS,OAAO,GAAG,OAAO,IAAI;IAC5D,KAAK,EAAE,qBAAqB;IAC5B;QACE,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;QACjC,eAAe,CAAC,EAAE,sBAAsB,CAAC;KAC1C;CACF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAAI,GAAG,SAAS,OAAO,GAAG,OAAO,EAC5D,SAAS,OAAO,EAChB,+LAUG,uBAA4B,KAC9B,gBAAgB,CAAC,GAAG,CA6ItB,CAAC"}
1
+ {"version":3,"file":"useCSSTransition.d.ts","sourceRoot":"","sources":["../../../src/lib/animation/useCSSTransition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,sBAAsB,EAAoB,MAAM,OAAO,CAAC;AAS5F,MAAM,MAAM,qBAAqB,GAC7B,QAAQ,GACR,WAAW,GACX,UAAU,GACV,OAAO,GACP,UAAU,GACV,SAAS,GACT,MAAM,GACN,SAAS,GACT,QAAQ,CAAC;AAEb,MAAM,MAAM,uBAAuB,GAAG;IACpC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACrC,UAAU,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,SAAS,CAAC,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9D,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,QAAQ,CAAC,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAAC,GAAG,SAAS,OAAO,GAAG,OAAO,IAAI;IAC5D,KAAK,EAAE,qBAAqB;IAC5B;QACE,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;QACjC,eAAe,CAAC,EAAE,sBAAsB,CAAC;KAC1C;CACF,CAAC;AAIF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAAI,GAAG,SAAS,OAAO,GAAG,OAAO,EAC5D,SAAS,OAAO,EAChB,+LAUG,uBAA4B,KAC9B,gBAAgB,CAAC,GAAG,CAiLtB,CAAC"}
@@ -1,8 +1,10 @@
1
- import { useEffect, useRef } from "react";
1
+ import { useRef, useState } from "react";
2
2
  import { noop } from "@vkontakte/vkjs";
3
3
  import { useStableCallback } from "../../hooks/useStableCallback.js";
4
- import { useStateWithPrev } from "../../hooks/useStateWithPrev.js";
4
+ import { millisecondsInSecond } from "../date.js";
5
+ import { useIsomorphicLayoutEffect } from "../useIsomorphicLayoutEffect.js";
5
6
  /* istanbul ignore next: особенность рендера в браузере когда меняется className, в Jest не воспроизвести */ const forceReflowForFixNewMountedElement = (node)=>void (node === null || node === void 0 ? void 0 : node.scrollTop);
7
+ const TRANSITION_FALLBACK_DELAY = 100;
6
8
  /**
7
9
  * Хук основан на компоненте `CSSTransition` из библиотеки `react-transition-group`.
8
10
  *
@@ -16,8 +18,9 @@ import { useStateWithPrev } from "../../hooks/useStateWithPrev.js";
16
18
  const onExit = useStableCallback(onExitProp || noop);
17
19
  const onExiting = useStableCallback(onExitingProp || noop);
18
20
  const onExited = useStableCallback(onExitedProp || noop);
21
+ const timerRef = useRef(null);
19
22
  const ref = useRef(null);
20
- const [[state, prevState], setState] = useStateWithPrev(()=>{
23
+ const [state, setState] = useState(()=>{
21
24
  if (!inProp) {
22
25
  return 'exited';
23
26
  }
@@ -27,7 +30,13 @@ import { useStateWithPrev } from "../../hooks/useStateWithPrev.js";
27
30
  }
28
31
  return 'entered';
29
32
  });
30
- useEffect(function updateState() {
33
+ const clearTimer = ()=>{
34
+ if (timerRef.current) {
35
+ clearTimeout(timerRef.current);
36
+ timerRef.current = null;
37
+ }
38
+ };
39
+ useIsomorphicLayoutEffect(function updateState() {
31
40
  if (inProp) {
32
41
  switch(state){
33
42
  case 'appear':
@@ -91,8 +100,6 @@ import { useStateWithPrev } from "../../hooks/useStateWithPrev.js";
91
100
  }, [
92
101
  inProp,
93
102
  state,
94
- prevState,
95
- setState,
96
103
  enableAppear,
97
104
  enableEnter,
98
105
  onEnter,
@@ -103,30 +110,52 @@ import { useStateWithPrev } from "../../hooks/useStateWithPrev.js";
103
110
  onExiting,
104
111
  onExited
105
112
  ]);
106
- const onTransitionEnd = (event)=>{
107
- /* istanbul ignore if: на всякий случай предупреждаем всплытие, нет смысла проверять условие */ if (event.target !== ref.current) {
108
- return;
109
- }
113
+ const completeTransition = useStableCallback((event)=>{
114
+ clearTimer();
110
115
  switch(state){
111
116
  case 'appearing':
112
117
  setState('appeared');
113
- onEntered(event.propertyName, true);
118
+ onEntered(event === null || event === void 0 ? void 0 : event.propertyName, true);
114
119
  break;
115
120
  case 'entering':
116
121
  setState('entered');
117
- onEntered(event.propertyName);
122
+ onEntered(event === null || event === void 0 ? void 0 : event.propertyName);
118
123
  break;
119
124
  case 'exiting':
120
125
  setState('exited');
121
- onExited(event.propertyName);
126
+ onExited(event === null || event === void 0 ? void 0 : event.propertyName);
122
127
  break;
123
128
  }
124
- };
129
+ });
130
+ useIsomorphicLayoutEffect(function scheduleTransitionCompletionFallback() {
131
+ const el = ref.current;
132
+ if (!el) {
133
+ return;
134
+ }
135
+ if (state === 'appearing' || state === 'entering' || state === 'exiting') {
136
+ const style = getComputedStyle(el);
137
+ const parseTime = (s)=>s.includes('ms') ? parseFloat(s) : parseFloat(s) * millisecondsInSecond;
138
+ const duration = Math.max(...style.transitionDuration.split(',').map(parseTime)) + Math.max(...style.transitionDelay.split(',').map(parseTime));
139
+ if (duration <= 0) {
140
+ completeTransition();
141
+ return;
142
+ }
143
+ // fallback если onTransitionEnd не пришёл
144
+ // TRANSITION_FALLBACK_DELAY, чтобы минимизировать вероятность,
145
+ // что setTimeout сработает раньше onTransitionEnd
146
+ timerRef.current = setTimeout(completeTransition, duration + TRANSITION_FALLBACK_DELAY);
147
+ return clearTimer;
148
+ }
149
+ return;
150
+ }, [
151
+ completeTransition,
152
+ state
153
+ ]);
125
154
  return [
126
155
  state,
127
156
  {
128
157
  ref,
129
- onTransitionEnd: state !== 'appeared' && state !== 'entered' && state !== 'exited' ? onTransitionEnd : undefined
158
+ onTransitionEnd: state !== 'appeared' && state !== 'entered' && state !== 'exited' ? completeTransition : undefined
130
159
  }
131
160
  ];
132
161
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/animation/useCSSTransition.ts"],"sourcesContent":["import { type TransitionEvent, type TransitionEventHandler, useEffect, useRef } from 'react';\nimport { noop } from '@vkontakte/vkjs';\nimport { useStableCallback } from '../../hooks/useStableCallback';\nimport { useStateWithPrev } from '../../hooks/useStateWithPrev';\n\n/* istanbul ignore next: особенность рендера в браузере когда меняется className, в Jest не воспроизвести */\nconst forceReflowForFixNewMountedElement = (node: Element | null) => void node?.scrollTop;\n\nexport type UseCSSTransitionState =\n | 'appear'\n | 'appearing'\n | 'appeared'\n | 'enter'\n | 'entering'\n | 'entered'\n | 'exit'\n | 'exiting'\n | 'exited';\n\nexport type UseCSSTransitionOptions = {\n enableAppear?: boolean;\n enableEnter?: boolean;\n enableExit?: boolean;\n onEnter?: (appear?: boolean) => void;\n onEntering?: (appear?: boolean) => void;\n onEntered?: (propertyName?: string, appear?: boolean) => void;\n onExit?: () => void;\n onExiting?: () => void;\n onExited?: (propertyName?: string) => void;\n};\n\nexport type UseCSSTransition<Ref extends Element = Element> = [\n state: UseCSSTransitionState,\n {\n ref: React.RefObject<Ref | null>;\n onTransitionEnd?: TransitionEventHandler;\n },\n];\n\n/**\n * Хук основан на компоненте `CSSTransition` из библиотеки `react-transition-group`.\n *\n * @link https://reactcommunity.org/react-transition-group/css-transition\n *\n * @private\n */\nexport const useCSSTransition = <Ref extends Element = Element>(\n inProp?: boolean,\n {\n enableAppear = false,\n enableEnter = true,\n enableExit = true,\n onEnter: onEnterProp,\n onEntering: onEnteringProp,\n onEntered: onEnteredProp,\n onExit: onExitProp,\n onExiting: onExitingProp,\n onExited: onExitedProp,\n }: UseCSSTransitionOptions = {},\n): UseCSSTransition<Ref> => {\n const onEnter = useStableCallback(onEnterProp || noop);\n const onEntering = useStableCallback(onEnteringProp || noop);\n const onEntered = useStableCallback(onEnteredProp || noop);\n const onExit = useStableCallback(onExitProp || noop);\n const onExiting = useStableCallback(onExitingProp || noop);\n const onExited = useStableCallback(onExitedProp || noop);\n\n const ref = useRef<Ref | null>(null);\n const [[state, prevState], setState] = useStateWithPrev<UseCSSTransitionState>(() => {\n if (!inProp) {\n return 'exited';\n }\n\n if (enableAppear) {\n onEnter(true);\n return 'appear';\n }\n\n return 'entered';\n });\n\n useEffect(\n function updateState() {\n if (inProp) {\n switch (state) {\n case 'appear':\n forceReflowForFixNewMountedElement(ref.current);\n setState('appearing');\n onEntering(true);\n break;\n case 'enter':\n forceReflowForFixNewMountedElement(ref.current);\n setState('entering');\n onEntering();\n break;\n case 'exiting':\n if (enableEnter) {\n setState('entering');\n onEntering();\n break;\n }\n\n setState('entered');\n onEntered();\n break;\n case 'exited':\n if (enableEnter) {\n setState('enter');\n onEnter();\n break;\n }\n\n setState('entered');\n onEntered();\n break;\n }\n } else {\n switch (state) {\n case 'exit':\n forceReflowForFixNewMountedElement(ref.current);\n setState('exiting');\n onExiting();\n break;\n case 'appearing':\n case 'entering':\n if (enableExit) {\n setState('exiting');\n onExiting();\n break;\n }\n\n setState('exited');\n onExited();\n break;\n case 'appeared':\n case 'entered':\n if (enableExit) {\n setState('exit');\n onExit();\n break;\n }\n\n setState('exited');\n onExited();\n break;\n }\n }\n },\n [\n inProp,\n\n state,\n prevState,\n setState,\n\n enableAppear,\n enableEnter,\n onEnter,\n onEntering,\n onEntered,\n\n enableExit,\n onExit,\n onExiting,\n onExited,\n ],\n );\n\n const onTransitionEnd = (event: TransitionEvent) => {\n /* istanbul ignore if: на всякий случай предупреждаем всплытие, нет смысла проверять условие */\n if (event.target !== ref.current) {\n return;\n }\n\n switch (state) {\n case 'appearing':\n setState('appeared');\n onEntered(event.propertyName, true);\n break;\n case 'entering':\n setState('entered');\n onEntered(event.propertyName);\n break;\n case 'exiting':\n setState('exited');\n onExited(event.propertyName);\n break;\n }\n };\n\n return [\n state,\n {\n ref,\n onTransitionEnd:\n state !== 'appeared' && state !== 'entered' && state !== 'exited'\n ? onTransitionEnd\n : undefined,\n },\n ];\n};\n"],"names":["useEffect","useRef","noop","useStableCallback","useStateWithPrev","forceReflowForFixNewMountedElement","node","scrollTop","useCSSTransition","inProp","enableAppear","enableEnter","enableExit","onEnter","onEnterProp","onEntering","onEnteringProp","onEntered","onEnteredProp","onExit","onExitProp","onExiting","onExitingProp","onExited","onExitedProp","ref","state","prevState","setState","updateState","current","onTransitionEnd","event","target","propertyName","undefined"],"mappings":"AAAA,SAA4DA,SAAS,EAAEC,MAAM,QAAQ,QAAQ;AAC7F,SAASC,IAAI,QAAQ,kBAAkB;AACvC,SAASC,iBAAiB,QAAQ,mCAAgC;AAClE,SAASC,gBAAgB,QAAQ,kCAA+B;AAEhE,0GAA0G,GAC1G,MAAMC,qCAAqC,CAACC,OAAyB,MAAKA,iBAAAA,2BAAAA,KAAMC,SAAS;AAiCzF;;;;;;CAMC,GACD,OAAO,MAAMC,mBAAmB,CAC9BC,QACA,EACEC,eAAe,KAAK,EACpBC,cAAc,IAAI,EAClBC,aAAa,IAAI,EACjBC,SAASC,WAAW,EACpBC,YAAYC,cAAc,EAC1BC,WAAWC,aAAa,EACxBC,QAAQC,UAAU,EAClBC,WAAWC,aAAa,EACxBC,UAAUC,YAAY,EACE,GAAG,CAAC,CAAC;IAE/B,MAAMX,UAAUV,kBAAkBW,eAAeZ;IACjD,MAAMa,aAAaZ,kBAAkBa,kBAAkBd;IACvD,MAAMe,YAAYd,kBAAkBe,iBAAiBhB;IACrD,MAAMiB,SAAShB,kBAAkBiB,cAAclB;IAC/C,MAAMmB,YAAYlB,kBAAkBmB,iBAAiBpB;IACrD,MAAMqB,WAAWpB,kBAAkBqB,gBAAgBtB;IAEnD,MAAMuB,MAAMxB,OAAmB;IAC/B,MAAM,CAAC,CAACyB,OAAOC,UAAU,EAAEC,SAAS,GAAGxB,iBAAwC;QAC7E,IAAI,CAACK,QAAQ;YACX,OAAO;QACT;QAEA,IAAIC,cAAc;YAChBG,QAAQ;YACR,OAAO;QACT;QAEA,OAAO;IACT;IAEAb,UACE,SAAS6B;QACP,IAAIpB,QAAQ;YACV,OAAQiB;gBACN,KAAK;oBACHrB,mCAAmCoB,IAAIK,OAAO;oBAC9CF,SAAS;oBACTb,WAAW;oBACX;gBACF,KAAK;oBACHV,mCAAmCoB,IAAIK,OAAO;oBAC9CF,SAAS;oBACTb;oBACA;gBACF,KAAK;oBACH,IAAIJ,aAAa;wBACfiB,SAAS;wBACTb;wBACA;oBACF;oBAEAa,SAAS;oBACTX;oBACA;gBACF,KAAK;oBACH,IAAIN,aAAa;wBACfiB,SAAS;wBACTf;wBACA;oBACF;oBAEAe,SAAS;oBACTX;oBACA;YACJ;QACF,OAAO;YACL,OAAQS;gBACN,KAAK;oBACHrB,mCAAmCoB,IAAIK,OAAO;oBAC9CF,SAAS;oBACTP;oBACA;gBACF,KAAK;gBACL,KAAK;oBACH,IAAIT,YAAY;wBACdgB,SAAS;wBACTP;wBACA;oBACF;oBAEAO,SAAS;oBACTL;oBACA;gBACF,KAAK;gBACL,KAAK;oBACH,IAAIX,YAAY;wBACdgB,SAAS;wBACTT;wBACA;oBACF;oBAEAS,SAAS;oBACTL;oBACA;YACJ;QACF;IACF,GACA;QACEd;QAEAiB;QACAC;QACAC;QAEAlB;QACAC;QACAE;QACAE;QACAE;QAEAL;QACAO;QACAE;QACAE;KACD;IAGH,MAAMQ,kBAAkB,CAACC;QACvB,6FAA6F,GAC7F,IAAIA,MAAMC,MAAM,KAAKR,IAAIK,OAAO,EAAE;YAChC;QACF;QAEA,OAAQJ;YACN,KAAK;gBACHE,SAAS;gBACTX,UAAUe,MAAME,YAAY,EAAE;gBAC9B;YACF,KAAK;gBACHN,SAAS;gBACTX,UAAUe,MAAME,YAAY;gBAC5B;YACF,KAAK;gBACHN,SAAS;gBACTL,SAASS,MAAME,YAAY;gBAC3B;QACJ;IACF;IAEA,OAAO;QACLR;QACA;YACED;YACAM,iBACEL,UAAU,cAAcA,UAAU,aAAaA,UAAU,WACrDK,kBACAI;QACR;KACD;AACH,EAAE"}
1
+ {"version":3,"sources":["../../../src/lib/animation/useCSSTransition.ts"],"sourcesContent":["import { type TransitionEvent, type TransitionEventHandler, useRef, useState } from 'react';\nimport { noop } from '@vkontakte/vkjs';\nimport { useStableCallback } from '../../hooks/useStableCallback';\nimport { millisecondsInSecond } from '../date';\nimport { useIsomorphicLayoutEffect } from '../useIsomorphicLayoutEffect';\n\n/* istanbul ignore next: особенность рендера в браузере когда меняется className, в Jest не воспроизвести */\nconst forceReflowForFixNewMountedElement = (node: Element | null) => void node?.scrollTop;\n\nexport type UseCSSTransitionState =\n | 'appear'\n | 'appearing'\n | 'appeared'\n | 'enter'\n | 'entering'\n | 'entered'\n | 'exit'\n | 'exiting'\n | 'exited';\n\nexport type UseCSSTransitionOptions = {\n enableAppear?: boolean;\n enableEnter?: boolean;\n enableExit?: boolean;\n onEnter?: (appear?: boolean) => void;\n onEntering?: (appear?: boolean) => void;\n onEntered?: (propertyName?: string, appear?: boolean) => void;\n onExit?: () => void;\n onExiting?: () => void;\n onExited?: (propertyName?: string) => void;\n};\n\nexport type UseCSSTransition<Ref extends Element = Element> = [\n state: UseCSSTransitionState,\n {\n ref: React.RefObject<Ref | null>;\n onTransitionEnd?: TransitionEventHandler;\n },\n];\n\nconst TRANSITION_FALLBACK_DELAY = 100;\n\n/**\n * Хук основан на компоненте `CSSTransition` из библиотеки `react-transition-group`.\n *\n * @link https://reactcommunity.org/react-transition-group/css-transition\n *\n * @private\n */\nexport const useCSSTransition = <Ref extends Element = Element>(\n inProp?: boolean,\n {\n enableAppear = false,\n enableEnter = true,\n enableExit = true,\n onEnter: onEnterProp,\n onEntering: onEnteringProp,\n onEntered: onEnteredProp,\n onExit: onExitProp,\n onExiting: onExitingProp,\n onExited: onExitedProp,\n }: UseCSSTransitionOptions = {},\n): UseCSSTransition<Ref> => {\n const onEnter = useStableCallback(onEnterProp || noop);\n const onEntering = useStableCallback(onEnteringProp || noop);\n const onEntered = useStableCallback(onEnteredProp || noop);\n const onExit = useStableCallback(onExitProp || noop);\n const onExiting = useStableCallback(onExitingProp || noop);\n const onExited = useStableCallback(onExitedProp || noop);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const ref = useRef<Ref | null>(null);\n const [state, setState] = useState<UseCSSTransitionState>(() => {\n if (!inProp) {\n return 'exited';\n }\n\n if (enableAppear) {\n onEnter(true);\n return 'appear';\n }\n\n return 'entered';\n });\n\n const clearTimer = () => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n };\n\n useIsomorphicLayoutEffect(\n function updateState() {\n if (inProp) {\n switch (state) {\n case 'appear':\n forceReflowForFixNewMountedElement(ref.current);\n setState('appearing');\n onEntering(true);\n break;\n case 'enter':\n forceReflowForFixNewMountedElement(ref.current);\n setState('entering');\n onEntering();\n break;\n case 'exiting':\n if (enableEnter) {\n setState('entering');\n onEntering();\n break;\n }\n\n setState('entered');\n onEntered();\n break;\n case 'exited':\n if (enableEnter) {\n setState('enter');\n onEnter();\n break;\n }\n\n setState('entered');\n onEntered();\n break;\n }\n } else {\n switch (state) {\n case 'exit':\n forceReflowForFixNewMountedElement(ref.current);\n setState('exiting');\n onExiting();\n break;\n case 'appearing':\n case 'entering':\n if (enableExit) {\n setState('exiting');\n onExiting();\n break;\n }\n\n setState('exited');\n onExited();\n break;\n case 'appeared':\n case 'entered':\n if (enableExit) {\n setState('exit');\n onExit();\n break;\n }\n\n setState('exited');\n onExited();\n break;\n }\n }\n },\n [\n inProp,\n\n state,\n enableAppear,\n enableEnter,\n onEnter,\n onEntering,\n onEntered,\n\n enableExit,\n onExit,\n onExiting,\n onExited,\n ],\n );\n\n const completeTransition = useStableCallback((event?: TransitionEvent) => {\n clearTimer();\n\n switch (state) {\n case 'appearing':\n setState('appeared');\n onEntered(event?.propertyName, true);\n break;\n case 'entering':\n setState('entered');\n onEntered(event?.propertyName);\n break;\n case 'exiting':\n setState('exited');\n onExited(event?.propertyName);\n break;\n }\n });\n\n useIsomorphicLayoutEffect(\n function scheduleTransitionCompletionFallback() {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n if (state === 'appearing' || state === 'entering' || state === 'exiting') {\n const style = getComputedStyle(el);\n\n const parseTime = (s: string) =>\n s.includes('ms') ? parseFloat(s) : parseFloat(s) * millisecondsInSecond;\n\n const duration =\n Math.max(...style.transitionDuration.split(',').map(parseTime)) +\n Math.max(...style.transitionDelay.split(',').map(parseTime));\n\n if (duration <= 0) {\n completeTransition();\n return;\n }\n\n // fallback если onTransitionEnd не пришёл\n // TRANSITION_FALLBACK_DELAY, чтобы минимизировать вероятность,\n // что setTimeout сработает раньше onTransitionEnd\n timerRef.current = setTimeout(completeTransition, duration + TRANSITION_FALLBACK_DELAY);\n\n return clearTimer;\n }\n return;\n },\n [completeTransition, state],\n );\n\n return [\n state,\n {\n ref,\n onTransitionEnd:\n state !== 'appeared' && state !== 'entered' && state !== 'exited'\n ? completeTransition\n : undefined,\n },\n ];\n};\n"],"names":["useRef","useState","noop","useStableCallback","millisecondsInSecond","useIsomorphicLayoutEffect","forceReflowForFixNewMountedElement","node","scrollTop","TRANSITION_FALLBACK_DELAY","useCSSTransition","inProp","enableAppear","enableEnter","enableExit","onEnter","onEnterProp","onEntering","onEnteringProp","onEntered","onEnteredProp","onExit","onExitProp","onExiting","onExitingProp","onExited","onExitedProp","timerRef","ref","state","setState","clearTimer","current","clearTimeout","updateState","completeTransition","event","propertyName","scheduleTransitionCompletionFallback","el","style","getComputedStyle","parseTime","s","includes","parseFloat","duration","Math","max","transitionDuration","split","map","transitionDelay","setTimeout","onTransitionEnd","undefined"],"mappings":"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"}