@vkontakte/vkui 7.6.1 → 7.6.3
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.
- package/dist/components/Touch/Touch.d.ts.map +1 -1
- package/dist/components/Touch/Touch.js +12 -5
- package/dist/components/Touch/Touch.js.map +1 -1
- package/dist/components.css +1 -1
- package/dist/components.css.map +1 -1
- package/dist/cssm/components/Button/Button.module.css +36 -12
- package/dist/cssm/components/Touch/Touch.js +12 -5
- package/dist/cssm/components/Touch/Touch.js.map +1 -1
- package/dist/cssm/lib/animation/useCSSTransition.js +44 -15
- package/dist/cssm/lib/animation/useCSSTransition.js.map +1 -1
- package/dist/lib/animation/useCSSTransition.d.ts.map +1 -1
- package/dist/lib/animation/useCSSTransition.js +44 -15
- package/dist/lib/animation/useCSSTransition.js.map +1 -1
- package/dist/vkui.css +1 -1
- package/dist/vkui.css.map +1 -1
- package/package.json +1 -1
- package/src/components/Button/Button.module.css +30 -12
- package/src/components/Button/Button.module.css.d.ts.map +1 -1
- package/src/components/Touch/Touch.tsx +14 -5
- package/src/lib/animation/useCSSTransition.ts +56 -17
|
@@ -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,
|
|
@@ -22,6 +22,9 @@ import { useIsomorphicLayoutEffect } from "../../lib/useIsomorphicLayoutEffect.j
|
|
|
22
22
|
}
|
|
23
23
|
};
|
|
24
24
|
React.useEffect(()=>cleanupTargetNativeGestureEvents, []);
|
|
25
|
+
const isTouchEvent = (event)=>{
|
|
26
|
+
return event.type.startsWith('touch');
|
|
27
|
+
};
|
|
25
28
|
/**
|
|
26
29
|
* Note: используем `useStableCallback()`, чтобы не терялась область видимости `onEnd`/`onEndX`/`onEndY`.
|
|
27
30
|
*/ const handleNativePointerUp = useStableCallback((event)=>{
|
|
@@ -36,7 +39,7 @@ import { useIsomorphicLayoutEffect } from "../../lib/useIsomorphicLayoutEffect.j
|
|
|
36
39
|
onEndY
|
|
37
40
|
], stopPropagation);
|
|
38
41
|
}
|
|
39
|
-
if (
|
|
42
|
+
if (isTouchEvent(event)) {
|
|
40
43
|
// https://github.com/VKCOM/VKUI/issues/4414
|
|
41
44
|
// если тач-устройство и был зафиксирован touchmove,
|
|
42
45
|
// то событие клика не вызывается
|
|
@@ -98,6 +101,10 @@ import { useIsomorphicLayoutEffect } from "../../lib/useIsomorphicLayoutEffect.j
|
|
|
98
101
|
}
|
|
99
102
|
});
|
|
100
103
|
const handlePointerDown = useStableCallback((event)=>{
|
|
104
|
+
// Если touchstart сэмулировало mousedown, то заканчиваем обработку
|
|
105
|
+
if (gestureRef.current !== null) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
101
108
|
const nativeEvent = 'nativeEvent' in event ? event.nativeEvent : event;
|
|
102
109
|
gestureRef.current = initGesture(coordX(nativeEvent), coordY(nativeEvent));
|
|
103
110
|
const shouldCallDirectionHandlerOnlyIsSlide = false;
|
|
@@ -112,7 +119,7 @@ import { useIsomorphicLayoutEffect } from "../../lib/useIsomorphicLayoutEffect.j
|
|
|
112
119
|
};
|
|
113
120
|
// FIXME: заменить touch/mouse-события ниже на pointer-события после того, как бразуеры из
|
|
114
121
|
// .browserslistrc начнут поддерживать его (см. https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events#browser_compatibility).
|
|
115
|
-
if (
|
|
122
|
+
if (isTouchEvent(nativeEvent)) {
|
|
116
123
|
if (isHTMLElement(event.target) || isSVGElement(event.target)) {
|
|
117
124
|
// Тач-события не всплывают, поэтому навешиваем события на целевой элемент
|
|
118
125
|
// см. #235, #1968, https://stackoverflow.com/a/45760014
|
|
@@ -196,9 +203,9 @@ import { useIsomorphicLayoutEffect } from "../../lib/useIsomorphicLayoutEffect.j
|
|
|
196
203
|
// onLeave
|
|
197
204
|
onPointerLeave: usePointerHover ? handlePointerLeave : undefined,
|
|
198
205
|
onMouseLeave: !usePointerHover ? handlePointerLeave : undefined,
|
|
199
|
-
// handlePointerDown
|
|
200
|
-
onMouseDownCapture:
|
|
201
|
-
onMouseDown: !
|
|
206
|
+
// handlePointerDown(onTouchStart устанавливается отдельно через initializeNativeTouchEventStartWithPassiveFalse)
|
|
207
|
+
onMouseDownCapture: useCapture ? handlePointerDown : undefined,
|
|
208
|
+
onMouseDown: !useCapture ? handlePointerDown : undefined
|
|
202
209
|
});
|
|
203
210
|
};
|
|
204
211
|
function initGesture(startX, startY) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/Touch/Touch.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useExternRef } from '../../hooks/useExternRef';\nimport { useStableCallback } from '../../hooks/useStableCallback';\nimport { getWindow, isHTMLElement, isSVGElement } from '../../lib/dom';\nimport { coordX, coordY, touchEnabled, type VKUITouchEvent } from '../../lib/touch';\nimport { useIsomorphicLayoutEffect } from '../../lib/useIsomorphicLayoutEffect';\nimport type { HasComponent, HasRootRef } from '../../types';\n\n/**\n * Костыль для правильной работы тайпскрипта.\n */\ntype HTMLorSVGElementWithEvents = {\n /**\n * AddEventListener.\n */\n addEventListener: (<K extends keyof HTMLElementEventMap>(\n type: K,\n listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any,\n options?: boolean | AddEventListenerOptions,\n ) => void) &\n (<K extends keyof SVGElementEventMap>(\n type: K,\n listener: (this: SVGElement, ev: SVGElementEventMap[K]) => any,\n options?: boolean | AddEventListenerOptions,\n ) => void);\n /**\n * RemoveEventListener.\n */\n removeEventListener: (<K extends keyof HTMLElementEventMap>(\n type: K,\n listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any,\n options?: boolean | EventListenerOptions,\n ) => void) &\n (<K extends keyof SVGElementEventMap>(\n type: K,\n listener: (this: SVGElement, ev: SVGElementEventMap[K]) => any,\n options?: boolean | EventListenerOptions,\n ) => void);\n};\n\nexport interface CustomTouchEvent extends Gesture {\n /**\n * Оригинальное событие.\n */\n originalEvent: VKUITouchEvent;\n}\n\nexport type HoverHandler = (outputEvent: MouseEvent) => void;\n\nexport type CustomTouchEventHandler = (event: CustomTouchEvent) => void;\n\nexport interface TouchProps\n extends React.AllHTMLAttributes<HTMLElement>,\n HasRootRef<HTMLElement>,\n HasComponent {\n /**\n * Использовать pointer-events для hover-состояний.\n * Работает на отключенных элементах (disabled inputs).\n */\n usePointerHover?: boolean;\n /**\n * Использовать фазу capture для событий.\n */\n useCapture?: boolean;\n /**\n * Порог расстояния в пикселях для активации свайпа.\n * @default 5\n */\n slideThreshold?: number;\n /**\n * Блокировать click-события после распознавания свайпа.\n */\n noSlideClick?: boolean;\n /**\n * Останавливать всплытие событий.\n */\n stopPropagation?: boolean;\n /**\n * Обработчик входа курсора в область.\n */\n onEnter?: HoverHandler;\n /**\n * Обработчик выхода курсора из области.\n */\n onLeave?: HoverHandler;\n /**\n * Общий обработчик начала взаимодействия.\n */\n onStart?: CustomTouchEventHandler;\n /**\n * Обработчик начала горизонтального перемещения.\n */\n onStartX?: CustomTouchEventHandler;\n /**\n * Обработчик начала вертикального перемещения.\n */\n onStartY?: CustomTouchEventHandler;\n /**\n * Общий обработчик перемещения.\n */\n onMove?: CustomTouchEventHandler;\n /**\n * Обработчик горизонтального перемещения.\n */\n onMoveX?: CustomTouchEventHandler;\n /**\n * Обработчик вертикального перемещения.\n */\n onMoveY?: CustomTouchEventHandler;\n /**\n * Общий обработчик завершения взаимодействия.\n */\n onEnd?: CustomTouchEventHandler;\n /**\n * Обработчик завершения горизонтального свайпа.\n */\n onEndX?: CustomTouchEventHandler;\n /**\n * Обработчик завершения вертикального свайпа.\n */\n onEndY?: CustomTouchEventHandler;\n}\n\nexport interface Gesture {\n /**\n * Начальная X-координата касания.\n */\n startX: number;\n\n /**\n * Начальная Y-координата касания.\n */\n startY: number;\n\n /**\n * Время начала взаимодействия.\n */\n startT: Date;\n\n /**\n * Длительность взаимодействия в миллисекундах.\n */\n duration: number;\n\n /**\n * Флаг активного нажатия.\n */\n isPressed: boolean;\n\n /**\n * Флаг вертикального перемещения.\n */\n isY: boolean;\n\n /**\n * Флаг горизонтального перемещения.\n */\n isX: boolean;\n\n /**\n * Флаг горизонтального свайпа.\n */\n isSlideX: boolean;\n\n /**\n * Флаг вертикального свайпа.\n */\n isSlideY: boolean;\n\n /**\n * Общий флаг свайпа (вертикального или горизонтального).\n */\n isSlide: boolean;\n\n /**\n * Текущая X-координата курсора/касания.\n */\n clientX: number;\n\n /**\n * Текущая Y-координата курсора/касания.\n */\n clientY: number;\n\n /**\n * Смещение по X относительно начальной точки.\n */\n shiftX: number;\n\n /**\n * Смещение по Y относительно начальной точки.\n */\n shiftY: number;\n\n /**\n * Абсолютное смещение по X.\n */\n shiftXAbs: number;\n\n /**\n * Абсолютное смещение по Y.\n */\n shiftYAbs: number;\n}\n\n/**\n * @see https://vkui.io/components/touch\n */\nexport const Touch = ({\n onStart,\n onStartX,\n onStartY,\n onMove,\n onMoveX,\n onMoveY,\n onEnter,\n onLeave,\n onEnd,\n onEndX,\n onEndY,\n onClickCapture,\n usePointerHover,\n slideThreshold = 5,\n useCapture = false,\n Component = 'div',\n getRootRef,\n noSlideClick = false,\n stopPropagation = false,\n ...restProps\n}: TouchProps) => {\n const hostRef = useExternRef(getRootRef);\n const [isTouchEnabled] = React.useState(touchEnabled);\n const gestureRef = React.useRef<Gesture | null>(null);\n const didSlide = React.useRef(false);\n const disposeTargetNativeGestureEvents = React.useRef<VoidFunction | null>(null);\n\n const cleanupTargetNativeGestureEvents = () => {\n gestureRef.current = null;\n if (disposeTargetNativeGestureEvents.current) {\n disposeTargetNativeGestureEvents.current();\n disposeTargetNativeGestureEvents.current = null;\n }\n };\n\n React.useEffect(() => cleanupTargetNativeGestureEvents, []);\n\n /**\n * Note: используем `useStableCallback()`, чтобы не терялась область видимости `onEnd`/`onEndX`/`onEndY`.\n */\n const handleNativePointerUp = useStableCallback((event: MouseEvent | TouchEvent) => {\n const gesture = gestureRef.current;\n\n /* istanbul ignore if: нужно для Typescript */\n if (!gesture) {\n return;\n }\n\n if (gesture.isPressed) {\n dispatchUserHandlers(event, gesture, [onEnd, onEndX, onEndY], stopPropagation);\n }\n\n if (isTouchEnabled) {\n // https://github.com/VKCOM/VKUI/issues/4414\n // если тач-устройство и был зафиксирован touchmove,\n // то событие клика не вызывается\n if (gesture.isSlide) {\n didSlide.current = false;\n }\n // Если это был тач-евент, симулируем отмену hover\n if (onLeave) {\n onLeave(event as MouseEvent);\n }\n } else {\n didSlide.current = Boolean(gesture.isSlide);\n }\n\n cleanupTargetNativeGestureEvents();\n });\n\n /**\n * Note: используем `useStableCallback()`, чтобы не терялась область видимости `onMove`/`onMoveX`/`onMoveY`.\n */\n const handleNativePointerMove = useStableCallback((event: MouseEvent | TouchEvent) => {\n const gesture = gestureRef.current;\n\n /* istanbul ignore if: нужно для Typescript */\n if (!gesture) {\n return;\n }\n\n const clientX = coordX(event);\n const clientY = coordY(event);\n\n // смещения\n const shiftX = clientX - gesture.startX;\n const shiftY = clientY - gesture.startY;\n\n // абсолютные значения смещений\n const shiftXAbs = Math.abs(shiftX);\n const shiftYAbs = Math.abs(shiftY);\n\n // Если определяем мультитач, то прерываем жест\n if ('touches' in event && event.touches.length > 1) {\n return handleNativePointerUp(event);\n }\n\n // если мы ещё не определились\n if (!gesture.isX && !gesture.isY) {\n const willBeX = shiftXAbs >= slideThreshold && shiftXAbs > shiftYAbs;\n const willBeY = shiftYAbs >= slideThreshold && shiftYAbs > shiftXAbs;\n const willBeSlidedX = willBeX && (!!onMoveX || !!onMove);\n const willBeSlidedY = willBeY && (!!onMoveY || !!onMove);\n\n gesture.isY = willBeY;\n gesture.isX = willBeX;\n gesture.isSlideX = willBeSlidedX;\n gesture.isSlideY = willBeSlidedY;\n gesture.isSlide = willBeSlidedX || willBeSlidedY;\n }\n\n if (gesture.isSlide) {\n gesture.clientX = clientX;\n gesture.clientY = clientY;\n gesture.shiftX = shiftX;\n gesture.shiftY = shiftY;\n gesture.shiftXAbs = shiftXAbs;\n gesture.shiftYAbs = shiftYAbs;\n\n dispatchUserHandlers(event, gesture, [onMove, onMoveX, onMoveY], stopPropagation);\n }\n });\n\n const handlePointerDown = useStableCallback(\n (event: React.MouseEvent<HTMLElement> | React.TouchEvent<HTMLElement> | TouchEvent) => {\n const nativeEvent = 'nativeEvent' in event ? event.nativeEvent : event;\n\n gestureRef.current = initGesture(coordX(nativeEvent), coordY(nativeEvent));\n\n const shouldCallDirectionHandlerOnlyIsSlide = false;\n dispatchUserHandlers(\n event,\n gestureRef.current,\n [onStart, onStartX, onStartY],\n stopPropagation,\n shouldCallDirectionHandlerOnlyIsSlide,\n );\n\n const eventOptions = { capture: useCapture, passive: false };\n\n // FIXME: заменить touch/mouse-события ниже на pointer-события после того, как бразуеры из\n // .browserslistrc начнут поддерживать его (см. https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events#browser_compatibility).\n if (isTouchEnabled) {\n if (isHTMLElement(event.target) || isSVGElement(event.target)) {\n // Тач-события не всплывают, поэтому навешиваем события на целевой элемент\n // см. #235, #1968, https://stackoverflow.com/a/45760014\n const target: HTMLorSVGElementWithEvents = event.target;\n\n target.addEventListener('touchmove', handleNativePointerMove, eventOptions);\n target.addEventListener('touchend', handleNativePointerUp, eventOptions);\n target.addEventListener('touchcancel', handleNativePointerUp, eventOptions);\n\n disposeTargetNativeGestureEvents.current = () => {\n target.removeEventListener('touchmove', handleNativePointerMove, eventOptions);\n target.removeEventListener('touchend', handleNativePointerUp, eventOptions);\n target.removeEventListener('touchcancel', handleNativePointerUp, eventOptions);\n };\n }\n } else {\n // Используем события на Document, т.к. mouse-события на целевом элементе могут теряться при\n // выходе за границы этого элемента.\n const doc = getWindow(event.currentTarget).document;\n\n doc.addEventListener('mousemove', handleNativePointerMove, eventOptions);\n doc.addEventListener('mouseup', handleNativePointerUp, eventOptions);\n doc.addEventListener('mouseleave', handleNativePointerUp, eventOptions);\n\n disposeTargetNativeGestureEvents.current = () => {\n doc.removeEventListener('mousemove', handleNativePointerMove, eventOptions);\n doc.removeEventListener('mouseup', handleNativePointerUp, eventOptions);\n doc.removeEventListener('mouseleave', handleNativePointerUp, eventOptions);\n };\n }\n },\n );\n\n const handlePointerEnter = onEnter\n ? (event: React.MouseEvent<HTMLElement>) => onEnter(event.nativeEvent)\n : undefined;\n\n const handlePointerLeave = onLeave\n ? (event: React.MouseEvent<HTMLElement>) => onLeave(event.nativeEvent)\n : undefined;\n\n /**\n * Отменяет нативное браузерное поведение для вложенных ссылок и изображений.\n */\n const handleDragStart = (event: React.DragEvent<HTMLElement>) => {\n const target = event.target as HTMLElement;\n if (target.tagName === 'A' || target.tagName === 'IMG') {\n event.preventDefault();\n }\n };\n\n /**\n * Отменяет переход по вложенной ссылке, если был зафиксирован свайп.\n */\n const handleClickCapture: typeof onClickCapture = (event) => {\n if (!didSlide.current) {\n return onClickCapture && onClickCapture(event);\n }\n\n if (noSlideClick) {\n event.stopPropagation();\n\n // https://github.com/VKCOM/VKUI/issues/1977\n // https://github.com/VKCOM/VKUI/issues/3892\n event.preventDefault();\n } else {\n onClickCapture && onClickCapture(event);\n }\n\n didSlide.current = false;\n };\n\n useIsomorphicLayoutEffect(\n function initializeNativeTouchStartEventWithPassiveFalse() {\n const hostEl = hostRef.current;\n if (!hostEl || !isTouchEnabled) {\n return;\n }\n\n const options = { capture: useCapture, passive: false };\n hostEl.addEventListener('touchstart', handlePointerDown, options);\n\n return () => {\n hostEl.removeEventListener('touchstart', handlePointerDown, options);\n };\n },\n [hostRef, isTouchEnabled, useCapture, handlePointerDown],\n );\n\n return (\n <Component\n {...restProps}\n ref={hostRef}\n onDragStart={handleDragStart}\n onClickCapture={handleClickCapture}\n // onEnter\n onPointerEnter={usePointerHover ? handlePointerEnter : undefined}\n onMouseEnter={!usePointerHover ? handlePointerEnter : undefined}\n // onLeave\n onPointerLeave={usePointerHover ? handlePointerLeave : undefined}\n onMouseLeave={!usePointerHover ? handlePointerLeave : undefined}\n // handlePointerDown (onTouchStart устанавливается отдельно через initializeNativeTouchEventStartWithPassiveFalse)\n onMouseDownCapture={!isTouchEnabled && useCapture ? handlePointerDown : undefined}\n onMouseDown={!isTouchEnabled && !useCapture ? handlePointerDown : undefined}\n />\n );\n};\n\nfunction initGesture(startX: number, startY: number): Gesture {\n return {\n startX,\n startY,\n startT: new Date(),\n duration: 0,\n isPressed: true,\n isY: false,\n isX: false,\n isSlideX: false,\n isSlideY: false,\n isSlide: false,\n clientX: 0,\n clientY: 0,\n shiftX: 0,\n shiftY: 0,\n shiftXAbs: 0,\n shiftYAbs: 0,\n };\n}\n\ntype Handlers = [\n CustomTouchEventHandler | undefined,\n CustomTouchEventHandler | undefined,\n CustomTouchEventHandler | undefined,\n];\n\nfunction dispatchUserHandlers(\n event: MouseEvent | TouchEvent | React.MouseEvent | React.TouchEvent,\n gesture: Gesture,\n [handler, handlerX, handlerY]: Handlers,\n stopPropagation?: boolean,\n shouldCallDirectionHandlerOnlyIsSlide = true,\n) {\n if (stopPropagation) {\n event.stopPropagation();\n }\n\n const data = {\n ...gesture,\n originalEvent: event as unknown as VKUITouchEvent,\n duration: Date.now() - gesture.startT.getTime(),\n };\n\n if (handler) {\n handler(data);\n }\n\n if (handlerX) {\n if (shouldCallDirectionHandlerOnlyIsSlide) {\n if (gesture.isSlideX) {\n handlerX(data);\n }\n } else {\n handlerX(data);\n }\n }\n\n if (handlerY) {\n if (shouldCallDirectionHandlerOnlyIsSlide) {\n if (gesture.isSlideY) {\n handlerY(data);\n }\n } else {\n handlerY(data);\n }\n }\n}\n"],"names":["React","useExternRef","useStableCallback","getWindow","isHTMLElement","isSVGElement","coordX","coordY","touchEnabled","useIsomorphicLayoutEffect","Touch","onStart","onStartX","onStartY","onMove","onMoveX","onMoveY","onEnter","onLeave","onEnd","onEndX","onEndY","onClickCapture","usePointerHover","slideThreshold","useCapture","Component","getRootRef","noSlideClick","stopPropagation","restProps","hostRef","isTouchEnabled","useState","gestureRef","useRef","didSlide","disposeTargetNativeGestureEvents","cleanupTargetNativeGestureEvents","current","useEffect","handleNativePointerUp","event","gesture","isPressed","dispatchUserHandlers","isSlide","Boolean","handleNativePointerMove","clientX","clientY","shiftX","startX","shiftY","startY","shiftXAbs","Math","abs","shiftYAbs","touches","length","isX","isY","willBeX","willBeY","willBeSlidedX","willBeSlidedY","isSlideX","isSlideY","handlePointerDown","nativeEvent","initGesture","shouldCallDirectionHandlerOnlyIsSlide","eventOptions","capture","passive","target","addEventListener","removeEventListener","doc","currentTarget","document","handlePointerEnter","undefined","handlePointerLeave","handleDragStart","tagName","preventDefault","handleClickCapture","initializeNativeTouchStartEventWithPassiveFalse","hostEl","options","ref","onDragStart","onPointerEnter","onMouseEnter","onPointerLeave","onMouseLeave","onMouseDownCapture","onMouseDown","startT","Date","duration","handler","handlerX","handlerY","data","originalEvent","now","getTime"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/Touch/Touch.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useExternRef } from '../../hooks/useExternRef';\nimport { useStableCallback } from '../../hooks/useStableCallback';\nimport { getWindow, isHTMLElement, isSVGElement } from '../../lib/dom';\nimport { coordX, coordY, touchEnabled, type VKUITouchEvent } from '../../lib/touch';\nimport { useIsomorphicLayoutEffect } from '../../lib/useIsomorphicLayoutEffect';\nimport type { HasComponent, HasRootRef } from '../../types';\n\n/**\n * Костыль для правильной работы тайпскрипта.\n */\ntype HTMLorSVGElementWithEvents = {\n /**\n * AddEventListener.\n */\n addEventListener: (<K extends keyof HTMLElementEventMap>(\n type: K,\n listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any,\n options?: boolean | AddEventListenerOptions,\n ) => void) &\n (<K extends keyof SVGElementEventMap>(\n type: K,\n listener: (this: SVGElement, ev: SVGElementEventMap[K]) => any,\n options?: boolean | AddEventListenerOptions,\n ) => void);\n /**\n * RemoveEventListener.\n */\n removeEventListener: (<K extends keyof HTMLElementEventMap>(\n type: K,\n listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any,\n options?: boolean | EventListenerOptions,\n ) => void) &\n (<K extends keyof SVGElementEventMap>(\n type: K,\n listener: (this: SVGElement, ev: SVGElementEventMap[K]) => any,\n options?: boolean | EventListenerOptions,\n ) => void);\n};\n\nexport interface CustomTouchEvent extends Gesture {\n /**\n * Оригинальное событие.\n */\n originalEvent: VKUITouchEvent;\n}\n\nexport type HoverHandler = (outputEvent: MouseEvent) => void;\n\nexport type CustomTouchEventHandler = (event: CustomTouchEvent) => void;\n\nexport interface TouchProps\n extends React.AllHTMLAttributes<HTMLElement>,\n HasRootRef<HTMLElement>,\n HasComponent {\n /**\n * Использовать pointer-events для hover-состояний.\n * Работает на отключенных элементах (disabled inputs).\n */\n usePointerHover?: boolean;\n /**\n * Использовать фазу capture для событий.\n */\n useCapture?: boolean;\n /**\n * Порог расстояния в пикселях для активации свайпа.\n * @default 5\n */\n slideThreshold?: number;\n /**\n * Блокировать click-события после распознавания свайпа.\n */\n noSlideClick?: boolean;\n /**\n * Останавливать всплытие событий.\n */\n stopPropagation?: boolean;\n /**\n * Обработчик входа курсора в область.\n */\n onEnter?: HoverHandler;\n /**\n * Обработчик выхода курсора из области.\n */\n onLeave?: HoverHandler;\n /**\n * Общий обработчик начала взаимодействия.\n */\n onStart?: CustomTouchEventHandler;\n /**\n * Обработчик начала горизонтального перемещения.\n */\n onStartX?: CustomTouchEventHandler;\n /**\n * Обработчик начала вертикального перемещения.\n */\n onStartY?: CustomTouchEventHandler;\n /**\n * Общий обработчик перемещения.\n */\n onMove?: CustomTouchEventHandler;\n /**\n * Обработчик горизонтального перемещения.\n */\n onMoveX?: CustomTouchEventHandler;\n /**\n * Обработчик вертикального перемещения.\n */\n onMoveY?: CustomTouchEventHandler;\n /**\n * Общий обработчик завершения взаимодействия.\n */\n onEnd?: CustomTouchEventHandler;\n /**\n * Обработчик завершения горизонтального свайпа.\n */\n onEndX?: CustomTouchEventHandler;\n /**\n * Обработчик завершения вертикального свайпа.\n */\n onEndY?: CustomTouchEventHandler;\n}\n\nexport interface Gesture {\n /**\n * Начальная X-координата касания.\n */\n startX: number;\n\n /**\n * Начальная Y-координата касания.\n */\n startY: number;\n\n /**\n * Время начала взаимодействия.\n */\n startT: Date;\n\n /**\n * Длительность взаимодействия в миллисекундах.\n */\n duration: number;\n\n /**\n * Флаг активного нажатия.\n */\n isPressed: boolean;\n\n /**\n * Флаг вертикального перемещения.\n */\n isY: boolean;\n\n /**\n * Флаг горизонтального перемещения.\n */\n isX: boolean;\n\n /**\n * Флаг горизонтального свайпа.\n */\n isSlideX: boolean;\n\n /**\n * Флаг вертикального свайпа.\n */\n isSlideY: boolean;\n\n /**\n * Общий флаг свайпа (вертикального или горизонтального).\n */\n isSlide: boolean;\n\n /**\n * Текущая X-координата курсора/касания.\n */\n clientX: number;\n\n /**\n * Текущая Y-координата курсора/касания.\n */\n clientY: number;\n\n /**\n * Смещение по X относительно начальной точки.\n */\n shiftX: number;\n\n /**\n * Смещение по Y относительно начальной точки.\n */\n shiftY: number;\n\n /**\n * Абсолютное смещение по X.\n */\n shiftXAbs: number;\n\n /**\n * Абсолютное смещение по Y.\n */\n shiftYAbs: number;\n}\n\n/**\n * @see https://vkui.io/components/touch\n */\nexport const Touch = ({\n onStart,\n onStartX,\n onStartY,\n onMove,\n onMoveX,\n onMoveY,\n onEnter,\n onLeave,\n onEnd,\n onEndX,\n onEndY,\n onClickCapture,\n usePointerHover,\n slideThreshold = 5,\n useCapture = false,\n Component = 'div',\n getRootRef,\n noSlideClick = false,\n stopPropagation = false,\n ...restProps\n}: TouchProps) => {\n const hostRef = useExternRef(getRootRef);\n const [isTouchEnabled] = React.useState(touchEnabled);\n const gestureRef = React.useRef<Gesture | null>(null);\n const didSlide = React.useRef(false);\n const disposeTargetNativeGestureEvents = React.useRef<VoidFunction | null>(null);\n\n const cleanupTargetNativeGestureEvents = () => {\n gestureRef.current = null;\n if (disposeTargetNativeGestureEvents.current) {\n disposeTargetNativeGestureEvents.current();\n disposeTargetNativeGestureEvents.current = null;\n }\n };\n\n React.useEffect(() => cleanupTargetNativeGestureEvents, []);\n\n const isTouchEvent = (event: MouseEvent | TouchEvent) => {\n return event.type.startsWith('touch');\n };\n\n /**\n * Note: используем `useStableCallback()`, чтобы не терялась область видимости `onEnd`/`onEndX`/`onEndY`.\n */\n const handleNativePointerUp = useStableCallback((event: MouseEvent | TouchEvent) => {\n const gesture = gestureRef.current;\n\n /* istanbul ignore if: нужно для Typescript */\n if (!gesture) {\n return;\n }\n\n if (gesture.isPressed) {\n dispatchUserHandlers(event, gesture, [onEnd, onEndX, onEndY], stopPropagation);\n }\n\n if (isTouchEvent(event)) {\n // https://github.com/VKCOM/VKUI/issues/4414\n // если тач-устройство и был зафиксирован touchmove,\n // то событие клика не вызывается\n if (gesture.isSlide) {\n didSlide.current = false;\n }\n // Если это был тач-евент, симулируем отмену hover\n if (onLeave) {\n onLeave(event as MouseEvent);\n }\n } else {\n didSlide.current = Boolean(gesture.isSlide);\n }\n\n cleanupTargetNativeGestureEvents();\n });\n\n /**\n * Note: используем `useStableCallback()`, чтобы не терялась область видимости `onMove`/`onMoveX`/`onMoveY`.\n */\n const handleNativePointerMove = useStableCallback((event: MouseEvent | TouchEvent) => {\n const gesture = gestureRef.current;\n\n /* istanbul ignore if: нужно для Typescript */\n if (!gesture) {\n return;\n }\n\n const clientX = coordX(event);\n const clientY = coordY(event);\n\n // смещения\n const shiftX = clientX - gesture.startX;\n const shiftY = clientY - gesture.startY;\n\n // абсолютные значения смещений\n const shiftXAbs = Math.abs(shiftX);\n const shiftYAbs = Math.abs(shiftY);\n\n // Если определяем мультитач, то прерываем жест\n if ('touches' in event && event.touches.length > 1) {\n return handleNativePointerUp(event);\n }\n\n // если мы ещё не определились\n if (!gesture.isX && !gesture.isY) {\n const willBeX = shiftXAbs >= slideThreshold && shiftXAbs > shiftYAbs;\n const willBeY = shiftYAbs >= slideThreshold && shiftYAbs > shiftXAbs;\n const willBeSlidedX = willBeX && (!!onMoveX || !!onMove);\n const willBeSlidedY = willBeY && (!!onMoveY || !!onMove);\n\n gesture.isY = willBeY;\n gesture.isX = willBeX;\n gesture.isSlideX = willBeSlidedX;\n gesture.isSlideY = willBeSlidedY;\n gesture.isSlide = willBeSlidedX || willBeSlidedY;\n }\n\n if (gesture.isSlide) {\n gesture.clientX = clientX;\n gesture.clientY = clientY;\n gesture.shiftX = shiftX;\n gesture.shiftY = shiftY;\n gesture.shiftXAbs = shiftXAbs;\n gesture.shiftYAbs = shiftYAbs;\n\n dispatchUserHandlers(event, gesture, [onMove, onMoveX, onMoveY], stopPropagation);\n }\n });\n\n const handlePointerDown = useStableCallback(\n (event: React.MouseEvent<HTMLElement> | React.TouchEvent<HTMLElement> | TouchEvent) => {\n // Если touchstart сэмулировало mousedown, то заканчиваем обработку\n if (gestureRef.current !== null) {\n return;\n }\n\n const nativeEvent = 'nativeEvent' in event ? event.nativeEvent : event;\n\n gestureRef.current = initGesture(coordX(nativeEvent), coordY(nativeEvent));\n\n const shouldCallDirectionHandlerOnlyIsSlide = false;\n dispatchUserHandlers(\n event,\n gestureRef.current,\n [onStart, onStartX, onStartY],\n stopPropagation,\n shouldCallDirectionHandlerOnlyIsSlide,\n );\n\n const eventOptions = { capture: useCapture, passive: false };\n\n // FIXME: заменить touch/mouse-события ниже на pointer-события после того, как бразуеры из\n // .browserslistrc начнут поддерживать его (см. https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events#browser_compatibility).\n if (isTouchEvent(nativeEvent)) {\n if (isHTMLElement(event.target) || isSVGElement(event.target)) {\n // Тач-события не всплывают, поэтому навешиваем события на целевой элемент\n // см. #235, #1968, https://stackoverflow.com/a/45760014\n const target: HTMLorSVGElementWithEvents = event.target;\n\n target.addEventListener('touchmove', handleNativePointerMove, eventOptions);\n target.addEventListener('touchend', handleNativePointerUp, eventOptions);\n target.addEventListener('touchcancel', handleNativePointerUp, eventOptions);\n\n disposeTargetNativeGestureEvents.current = () => {\n target.removeEventListener('touchmove', handleNativePointerMove, eventOptions);\n target.removeEventListener('touchend', handleNativePointerUp, eventOptions);\n target.removeEventListener('touchcancel', handleNativePointerUp, eventOptions);\n };\n }\n } else {\n // Используем события на Document, т.к. mouse-события на целевом элементе могут теряться при\n // выходе за границы этого элемента.\n const doc = getWindow(event.currentTarget).document;\n\n doc.addEventListener('mousemove', handleNativePointerMove, eventOptions);\n doc.addEventListener('mouseup', handleNativePointerUp, eventOptions);\n doc.addEventListener('mouseleave', handleNativePointerUp, eventOptions);\n\n disposeTargetNativeGestureEvents.current = () => {\n doc.removeEventListener('mousemove', handleNativePointerMove, eventOptions);\n doc.removeEventListener('mouseup', handleNativePointerUp, eventOptions);\n doc.removeEventListener('mouseleave', handleNativePointerUp, eventOptions);\n };\n }\n },\n );\n\n const handlePointerEnter = onEnter\n ? (event: React.MouseEvent<HTMLElement>) => onEnter(event.nativeEvent)\n : undefined;\n\n const handlePointerLeave = onLeave\n ? (event: React.MouseEvent<HTMLElement>) => onLeave(event.nativeEvent)\n : undefined;\n\n /**\n * Отменяет нативное браузерное поведение для вложенных ссылок и изображений.\n */\n const handleDragStart = (event: React.DragEvent<HTMLElement>) => {\n const target = event.target as HTMLElement;\n if (target.tagName === 'A' || target.tagName === 'IMG') {\n event.preventDefault();\n }\n };\n\n /**\n * Отменяет переход по вложенной ссылке, если был зафиксирован свайп.\n */\n const handleClickCapture: typeof onClickCapture = (event) => {\n if (!didSlide.current) {\n return onClickCapture && onClickCapture(event);\n }\n\n if (noSlideClick) {\n event.stopPropagation();\n\n // https://github.com/VKCOM/VKUI/issues/1977\n // https://github.com/VKCOM/VKUI/issues/3892\n event.preventDefault();\n } else {\n onClickCapture && onClickCapture(event);\n }\n\n didSlide.current = false;\n };\n\n useIsomorphicLayoutEffect(\n function initializeNativeTouchStartEventWithPassiveFalse() {\n const hostEl = hostRef.current;\n if (!hostEl || !isTouchEnabled) {\n return;\n }\n\n const options = { capture: useCapture, passive: false };\n hostEl.addEventListener('touchstart', handlePointerDown, options);\n\n return () => {\n hostEl.removeEventListener('touchstart', handlePointerDown, options);\n };\n },\n [hostRef, isTouchEnabled, useCapture, handlePointerDown],\n );\n\n return (\n <Component\n {...restProps}\n ref={hostRef}\n onDragStart={handleDragStart}\n onClickCapture={handleClickCapture}\n // onEnter\n onPointerEnter={usePointerHover ? handlePointerEnter : undefined}\n onMouseEnter={!usePointerHover ? handlePointerEnter : undefined}\n // onLeave\n onPointerLeave={usePointerHover ? handlePointerLeave : undefined}\n onMouseLeave={!usePointerHover ? handlePointerLeave : undefined}\n // handlePointerDown(onTouchStart устанавливается отдельно через initializeNativeTouchEventStartWithPassiveFalse)\n onMouseDownCapture={useCapture ? handlePointerDown : undefined}\n onMouseDown={!useCapture ? handlePointerDown : undefined}\n />\n );\n};\n\nfunction initGesture(startX: number, startY: number): Gesture {\n return {\n startX,\n startY,\n startT: new Date(),\n duration: 0,\n isPressed: true,\n isY: false,\n isX: false,\n isSlideX: false,\n isSlideY: false,\n isSlide: false,\n clientX: 0,\n clientY: 0,\n shiftX: 0,\n shiftY: 0,\n shiftXAbs: 0,\n shiftYAbs: 0,\n };\n}\n\ntype Handlers = [\n CustomTouchEventHandler | undefined,\n CustomTouchEventHandler | undefined,\n CustomTouchEventHandler | undefined,\n];\n\nfunction dispatchUserHandlers(\n event: MouseEvent | TouchEvent | React.MouseEvent | React.TouchEvent,\n gesture: Gesture,\n [handler, handlerX, handlerY]: Handlers,\n stopPropagation?: boolean,\n shouldCallDirectionHandlerOnlyIsSlide = true,\n) {\n if (stopPropagation) {\n event.stopPropagation();\n }\n\n const data = {\n ...gesture,\n originalEvent: event as unknown as VKUITouchEvent,\n duration: Date.now() - gesture.startT.getTime(),\n };\n\n if (handler) {\n handler(data);\n }\n\n if (handlerX) {\n if (shouldCallDirectionHandlerOnlyIsSlide) {\n if (gesture.isSlideX) {\n handlerX(data);\n }\n } else {\n handlerX(data);\n }\n }\n\n if (handlerY) {\n if (shouldCallDirectionHandlerOnlyIsSlide) {\n if (gesture.isSlideY) {\n handlerY(data);\n }\n } else {\n handlerY(data);\n }\n }\n}\n"],"names":["React","useExternRef","useStableCallback","getWindow","isHTMLElement","isSVGElement","coordX","coordY","touchEnabled","useIsomorphicLayoutEffect","Touch","onStart","onStartX","onStartY","onMove","onMoveX","onMoveY","onEnter","onLeave","onEnd","onEndX","onEndY","onClickCapture","usePointerHover","slideThreshold","useCapture","Component","getRootRef","noSlideClick","stopPropagation","restProps","hostRef","isTouchEnabled","useState","gestureRef","useRef","didSlide","disposeTargetNativeGestureEvents","cleanupTargetNativeGestureEvents","current","useEffect","isTouchEvent","event","type","startsWith","handleNativePointerUp","gesture","isPressed","dispatchUserHandlers","isSlide","Boolean","handleNativePointerMove","clientX","clientY","shiftX","startX","shiftY","startY","shiftXAbs","Math","abs","shiftYAbs","touches","length","isX","isY","willBeX","willBeY","willBeSlidedX","willBeSlidedY","isSlideX","isSlideY","handlePointerDown","nativeEvent","initGesture","shouldCallDirectionHandlerOnlyIsSlide","eventOptions","capture","passive","target","addEventListener","removeEventListener","doc","currentTarget","document","handlePointerEnter","undefined","handlePointerLeave","handleDragStart","tagName","preventDefault","handleClickCapture","initializeNativeTouchStartEventWithPassiveFalse","hostEl","options","ref","onDragStart","onPointerEnter","onMouseEnter","onPointerLeave","onMouseLeave","onMouseDownCapture","onMouseDown","startT","Date","duration","handler","handlerX","handlerY","data","originalEvent","now","getTime"],"mappings":"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"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useRef, useState } from "react";
|
|
2
2
|
import { noop } from "@vkontakte/vkjs";
|
|
3
3
|
import { useStableCallback } from "../../hooks/useStableCallback.js";
|
|
4
|
-
import {
|
|
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 [
|
|
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
|
-
|
|
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
|
|
107
|
-
|
|
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
|
|
118
|
+
onEntered(event?.propertyName, true);
|
|
114
119
|
break;
|
|
115
120
|
case 'entering':
|
|
116
121
|
setState('entered');
|
|
117
|
-
onEntered(event
|
|
122
|
+
onEntered(event?.propertyName);
|
|
118
123
|
break;
|
|
119
124
|
case 'exiting':
|
|
120
125
|
setState('exited');
|
|
121
|
-
onExited(event
|
|
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' ?
|
|
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,
|
|
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,
|
|
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 {
|
|
1
|
+
import { useRef, useState } from "react";
|
|
2
2
|
import { noop } from "@vkontakte/vkjs";
|
|
3
3
|
import { useStableCallback } from "../../hooks/useStableCallback.js";
|
|
4
|
-
import {
|
|
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 [
|
|
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
|
-
|
|
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
|
|
107
|
-
|
|
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' ?
|
|
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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"}
|