diff --git a/packages/webpack-plugin/lib/platform/template/wx/component-config/movable-view.js b/packages/webpack-plugin/lib/platform/template/wx/component-config/movable-view.js index 77d53b44e..f24b45bf8 100644 --- a/packages/webpack-plugin/lib/platform/template/wx/component-config/movable-view.js +++ b/packages/webpack-plugin/lib/platform/template/wx/component-config/movable-view.js @@ -2,6 +2,8 @@ const TAG_NAME = 'movable-view' module.exports = function ({ print }) { const aliEventLog = print({ platform: 'ali', tag: TAG_NAME, isError: false, type: 'event' }) + const androidEventLog = print({ platform: 'android', tag: TAG_NAME, isError: false, type: 'event' }) + const iosEventLog = print({ platform: 'ios', tag: TAG_NAME, isError: false, type: 'event' }) const qaPropLog = print({ platform: 'qa', tag: TAG_NAME, isError: false }) const androidPropLog = print({ platform: 'android', tag: TAG_NAME, isError: false }) const iosPropLog = print({ platform: 'ios', tag: TAG_NAME, isError: false }) @@ -27,7 +29,7 @@ module.exports = function ({ print }) { android: androidPropLog }, { - test: /^(inertia|damping|animation)$/, + test: /^(damping|friction|scale|scale-min|scale-max|scale-value)$/, ios: iosPropLog, android: androidPropLog } @@ -36,6 +38,11 @@ module.exports = function ({ print }) { { test: /^(htouchmove|vtouchmove)$/, ali: aliEventLog + }, + { + test: /^(bindscale)$/, + ios: iosEventLog, + android: androidEventLog } ] } diff --git a/packages/webpack-plugin/lib/platform/template/wx/component-config/scroll-view.js b/packages/webpack-plugin/lib/platform/template/wx/component-config/scroll-view.js index a5e426e39..0987235c6 100644 --- a/packages/webpack-plugin/lib/platform/template/wx/component-config/scroll-view.js +++ b/packages/webpack-plugin/lib/platform/template/wx/component-config/scroll-view.js @@ -53,7 +53,7 @@ module.exports = function ({ print }) { qa: qaPropLog }, { - test: /^(scroll-into-view|refresher-threshold|enable-passive|scroll-anchoring|using-sticky|fast-deceleration|enable-flex)$/, + test: /^(refresher-threshold|enable-passive|scroll-anchoring|using-sticky|fast-deceleration|enable-flex)$/, android: androidPropLog, ios: iosPropLog }, diff --git a/packages/webpack-plugin/lib/runtime/components/react/context.ts b/packages/webpack-plugin/lib/runtime/components/react/context.ts index 5bb14fc15..39472e674 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/context.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/context.ts @@ -33,6 +33,10 @@ export interface IntersectionObserver { } } +export interface ScrollViewContextValue { + gestureRef: React.RefObject | null +} + export const MovableAreaContext = createContext({ width: 0, height: 0 }) export const FormContext = createContext(null) @@ -52,3 +56,5 @@ export const IntersectionObserverContext = createContext(null) export const KeyboardAvoidContext = createContext(null) + +export const ScrollViewContext = createContext({ gestureRef: null }) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-button.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-button.tsx index 004afef42..3d67acbeb 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-button.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-button.tsx @@ -34,7 +34,7 @@ * ✘ bindagreeprivacyauthorization * ✔ bindtap */ -import { createElement, useEffect, useRef, useState, ReactNode, forwardRef, useContext, JSX } from 'react' +import { createElement, useEffect, useRef, ReactNode, forwardRef, useContext, JSX } from 'react' import { View, StyleSheet, @@ -45,10 +45,12 @@ import { NativeSyntheticEvent } from 'react-native' import { warn } from '@mpxjs/utils' -import { getCurrentPage, splitProps, splitStyle, useLayout, useTransformStyle, wrapChildren, extendObject } from './utils' +import { GestureDetector } from 'react-native-gesture-handler' +import { getCurrentPage, splitProps, splitStyle, useLayout, useTransformStyle, wrapChildren, extendObject, useHoverStyle } from './utils' import useInnerProps, { getCustomEvent } from './getInnerListeners' import useNodesRef, { HandlerRef } from './useNodesRef' import { RouteContext, FormContext } from './context' +import type { ExtendedViewStyle } from './types/common' export type Type = 'default' | 'primary' | 'warn' @@ -68,7 +70,7 @@ export interface ButtonProps { disabled?: boolean loading?: boolean 'hover-class'?: string - 'hover-style'?: ViewStyle & TextStyle & Record + 'hover-style'?: ExtendedViewStyle 'hover-start-time'?: number 'hover-stay-time'?: number 'open-type'?: OpenType @@ -83,8 +85,6 @@ export interface ButtonProps { children: ReactNode bindgetuserinfo?: (userInfo: any) => void bindtap?: (evt: NativeSyntheticEvent | unknown) => void - bindtouchstart?: (evt: NativeSyntheticEvent | unknown) => void - bindtouchend?: (evt: NativeSyntheticEvent | unknown) => void } const LOADING_IMAGE_URI = @@ -216,15 +216,15 @@ const Button = forwardRef, ButtonProps>((buttonPro style = {}, children, bindgetuserinfo, - bindtap, - bindtouchstart, - bindtouchend + bindtap } = props const pageId = useContext(RouteContext) const formContext = useContext(FormContext) + const { isHover, enableHoverStyle, gesture } = useHoverStyle({ hoverStyle, hoverStartTime, hoverStayTime, disabled }) + let submitFn: () => void | undefined let resetFn: () => void | undefined @@ -233,16 +233,6 @@ const Button = forwardRef, ButtonProps>((buttonPro resetFn = formContext.reset } - const refs = useRef<{ - hoverStartTimer: ReturnType | undefined - hoverStayTimer: ReturnType | undefined - }>({ - hoverStartTimer: undefined, - hoverStayTimer: undefined - }) - - const [isHover, setIsHover] = useState(false) - const isMiniSize = size === 'mini' const applyHoverEffect = isHover && hoverClass !== 'none' @@ -366,34 +356,6 @@ const Button = forwardRef, ButtonProps>((buttonPro } } - const setStayTimer = () => { - clearTimeout(refs.current.hoverStayTimer) - refs.current.hoverStayTimer = setTimeout(() => { - setIsHover(false) - clearTimeout(refs.current.hoverStayTimer) - }, hoverStayTime) - } - - const setStartTimer = () => { - clearTimeout(refs.current.hoverStartTimer) - refs.current.hoverStartTimer = setTimeout(() => { - setIsHover(true) - clearTimeout(refs.current.hoverStartTimer) - }, hoverStartTime) - } - - const onTouchStart = (evt: NativeSyntheticEvent) => { - bindtouchstart && bindtouchstart(evt) - if (disabled) return - setStartTimer() - } - - const onTouchEnd = (evt: NativeSyntheticEvent) => { - bindtouchend && bindtouchend(evt) - if (disabled) return - setStayTimer() - } - const handleFormTypeFn = () => { if (formType === 'submit') { submitFn && submitFn() @@ -418,8 +380,6 @@ const Button = forwardRef, ButtonProps>((buttonPro }, layoutProps, { - bindtouchstart: (bindtouchstart || !disabled) && onTouchStart, - bindtouchend: (bindtouchend || !disabled) && onTouchEnd, bindtap: !disabled && onTap } ), @@ -442,7 +402,7 @@ const Button = forwardRef, ButtonProps>((buttonPro } ) - return createElement(View, innerProps, loading && createElement(Loading, { alone: !children }), + const baseButton = createElement(View, innerProps, loading && createElement(Loading, { alone: !children }), wrapChildren( props, { @@ -453,6 +413,10 @@ const Button = forwardRef, ButtonProps>((buttonPro } ) ) + + return enableHoverStyle + ? createElement(GestureDetector, { gesture }, baseButton) + : baseButton }) Button.displayName = 'MpxButton' diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-movable-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-movable-view.tsx index 3a0a33025..38c4c64cb 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-movable-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-movable-view.tsx @@ -11,7 +11,7 @@ * ✘ scale-min * ✘ scale-max * ✘ scale-value - * ✘ animation + * ✔ animation * ✔ bindchange * ✘ bindscale * ✔ htouchmove diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-rich-text/index.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-rich-text/index.tsx index db6097df5..7e43b48c4 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-rich-text/index.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-rich-text/index.tsx @@ -3,10 +3,10 @@ * ✔ nodes */ import { View, ViewProps, ViewStyle } from 'react-native' -import { useRef, forwardRef, JSX, useState } from 'react' +import { useRef, forwardRef, JSX, useState, createElement } from 'react' import useInnerProps from '../getInnerListeners' import useNodesRef, { HandlerRef } from '../useNodesRef' // 引入辅助函数 -import { useTransformStyle, useLayout } from '../utils' +import { useTransformStyle, useLayout, extendObject } from '../utils' import { WebView, WebViewMessageEvent } from 'react-native-webview' import { generateHTML } from './html' @@ -91,28 +91,22 @@ const _RichText = forwardRef, _RichTextProps>(( layoutRef }) - const innerProps = useInnerProps(props, { + const innerProps = useInnerProps(props, extendObject({ ref: nodeRef, - style: { ...normalStyle, ...layoutStyle }, - ...layoutProps - }, [], { + style: extendObject(normalStyle, layoutStyle) + }, layoutProps), [], { layoutRef }) const html: string = typeof nodes === 'string' ? nodes : jsonToHtmlStr(nodes) - return ( - - { - setWebViewHeight(+event.nativeEvent.data) - }} - > - - + return createElement(View, innerProps, + createElement(WebView, { + source: { html: generateHTML(html) }, + onMessage: (event: WebViewMessageEvent) => { + setWebViewHeight(+event.nativeEvent.data) + } + }) ) }) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-scroll-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-scroll-view.tsx index b058e622d..a65adab4d 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-scroll-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-scroll-view.tsx @@ -33,13 +33,13 @@ */ import { ScrollView } from 'react-native-gesture-handler' import { View, RefreshControl, NativeSyntheticEvent, NativeScrollEvent, LayoutChangeEvent, ViewStyle } from 'react-native' -import { JSX, ReactNode, RefObject, useRef, useState, useEffect, forwardRef, useContext, createElement } from 'react' +import { JSX, ReactNode, RefObject, useRef, useState, useEffect, forwardRef, useContext, createElement, useMemo } from 'react' import { useAnimatedRef } from 'react-native-reanimated' import { warn } from '@mpxjs/utils' import useInnerProps, { getCustomEvent } from './getInnerListeners' import useNodesRef, { HandlerRef } from './useNodesRef' import { splitProps, splitStyle, useTransformStyle, useLayout, wrapChildren, extendObject, flatGesture, GestureHandler } from './utils' -import { IntersectionObserverContext } from './context' +import { IntersectionObserverContext, ScrollViewContext } from './context' interface ScrollViewProps { children?: ReactNode; @@ -194,6 +194,12 @@ const _ScrollView = forwardRef, S gestureRef: scrollViewRef }) + const contextValue = useMemo(() => { + return { + gestureRef: scrollViewRef + } + }, []) + const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout }) if (scrollX && scrollY) { @@ -507,14 +513,17 @@ const _ScrollView = forwardRef, S }, (refresherDefaultStyle && refresherDefaultStyle !== 'none' ? { colors: refreshColor[refresherDefaultStyle] } : null))) : undefined }), - wrapChildren( - props, - { - hasVarDec, - varContext: varContextRef.current, - textStyle, - textProps - } + createElement(ScrollViewContext.Provider, + { value: contextValue }, + wrapChildren( + props, + { + hasVarDec, + varContext: varContextRef.current, + textStyle, + textProps + } + ) ) ) }) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-view.tsx index cdc8c02ae..530f68f10 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-view.tsx @@ -12,9 +12,10 @@ import useAnimationHooks from './useAnimationHooks' import type { AnimationProp } from './useAnimationHooks' import { ExtendedViewStyle } from './types/common' import useNodesRef, { HandlerRef } from './useNodesRef' -import { parseUrl, PERCENT_REGEX, splitStyle, splitProps, useTransformStyle, wrapChildren, useLayout, renderImage, pickStyle, extendObject } from './utils' +import { parseUrl, PERCENT_REGEX, splitStyle, splitProps, useTransformStyle, wrapChildren, useLayout, renderImage, pickStyle, extendObject, useHoverStyle } from './utils' import { error } from '@mpxjs/utils' import LinearGradient from 'react-native-linear-gradient' +import { GestureDetector } from 'react-native-gesture-handler' export interface _ViewProps extends ViewProps { style?: ExtendedViewStyle @@ -642,7 +643,7 @@ function wrapImage (imageStyle?: ExtendedViewStyle, innerStyle?: Record @@ -652,6 +653,11 @@ interface WrapChildrenConfig { } function wrapWithChildren (props: _ViewProps, { hasVarDec, enableBackground, textStyle, backgroundStyle, varContext, textProps, innerStyle, enableFastImage }: WrapChildrenConfig) { + enableBackground = enableBackground || !!backgroundStyle + const enableBackgroundRef = useRef(enableBackground) + if (enableBackgroundRef.current !== enableBackground) { + error('[Mpx runtime error]: background use should be stable in the component lifecycle, or you can set [enable-background] with true.') + } const children = wrapChildren(props, { hasVarDec, varContext, @@ -667,7 +673,7 @@ function wrapWithChildren (props: _ViewProps, { hasVarDec, enableBackground, tex const _View = forwardRef, _ViewProps>((viewProps, ref): JSX.Element => { const { textProps, innerProps: props = {} } = splitProps(viewProps) - let { + const { style = {}, 'hover-style': hoverStyle, 'hover-start-time': hoverStartTime = 50, @@ -683,8 +689,6 @@ const _View = forwardRef, _ViewProps>((viewProps, r animation } = props - const [isHover, setIsHover] = useState(false) - // 默认样式 const defaultStyle: ExtendedViewStyle = style.display === 'flex' ? { @@ -695,6 +699,8 @@ const _View = forwardRef, _ViewProps>((viewProps, r } : {} + const { isHover, enableHoverStyle, gesture } = useHoverStyle({ hoverStyle, hoverStartTime, hoverStayTime }) + const styleObj: ExtendedViewStyle = extendObject({}, defaultStyle, style, isHover ? hoverStyle as ExtendedViewStyle : {}) const { @@ -714,56 +720,11 @@ const _View = forwardRef, _ViewProps>((viewProps, r const { textStyle, backgroundStyle, innerStyle = {} } = splitStyle(normalStyle) - enableBackground = enableBackground || !!backgroundStyle - const enableBackgroundRef = useRef(enableBackground) - if (enableBackgroundRef.current !== enableBackground) { - error('[Mpx runtime error]: background use should be stable in the component lifecycle, or you can set [enable-background] with true.') - } - const nodeRef = useRef(null) useNodesRef(props, ref, nodeRef, { style: normalStyle }) - const dataRef = useRef<{ - startTimer?: ReturnType - stayTimer?: ReturnType - }>({}) - - useEffect(() => { - return () => { - dataRef.current.startTimer && clearTimeout(dataRef.current.startTimer) - dataRef.current.stayTimer && clearTimeout(dataRef.current.stayTimer) - } - }, []) - - const setStartTimer = () => { - dataRef.current.startTimer && clearTimeout(dataRef.current.startTimer) - dataRef.current.startTimer = setTimeout(() => { - setIsHover(true) - }, +hoverStartTime) - } - - const setStayTimer = () => { - dataRef.current.stayTimer && clearTimeout(dataRef.current.stayTimer) - dataRef.current.startTimer && clearTimeout(dataRef.current.startTimer) - dataRef.current.stayTimer = setTimeout(() => { - setIsHover(false) - }, +hoverStayTime) - } - - function onTouchStart (e: NativeSyntheticEvent) { - const { bindtouchstart } = props - bindtouchstart && bindtouchstart(e) - setStartTimer() - } - - function onTouchEnd (e: NativeSyntheticEvent) { - const { bindtouchend } = props - bindtouchend && bindtouchend(e) - setStayTimer() - } - const { layoutRef, layoutStyle, @@ -772,30 +733,19 @@ const _View = forwardRef, _ViewProps>((viewProps, r const viewStyle = extendObject({}, innerStyle, layoutStyle) - enableAnimation = enableAnimation || !!animation - const enableAnimationRef = useRef(enableAnimation) - if (enableAnimationRef.current !== enableAnimation) { - error('[Mpx runtime error]: animation use should be stable in the component lifecycle, or you can set [enable-animation] with true.') - } - const finalStyle = enableAnimationRef.current - ? [viewStyle, useAnimationHooks({ - animation, - style: viewStyle - })] - : viewStyle + const { enableStyleAnimation, animationStyle } = useAnimationHooks({ + enableAnimation, + animation, + style: viewStyle + }) + const innerProps = useInnerProps( props, extendObject({ ref: nodeRef, - style: finalStyle + style: enableStyleAnimation ? [viewStyle, animationStyle] : viewStyle }, - layoutProps, - hoverStyle - ? { - bindtouchstart: onTouchStart, - bindtouchend: onTouchEnd - } - : {} + layoutProps ), [ 'hover-start-time', 'hover-stay-time', @@ -807,7 +757,7 @@ const _View = forwardRef, _ViewProps>((viewProps, r const childNode = wrapWithChildren(props, { hasVarDec, - enableBackground: enableBackgroundRef.current, + enableBackground, textStyle, backgroundStyle, varContext: varContextRef.current, @@ -816,9 +766,13 @@ const _View = forwardRef, _ViewProps>((viewProps, r enableFastImage }) - return enableAnimation + const BaseComponent = enableAnimation ? createElement(Animated.View, innerProps, childNode) : createElement(View, innerProps, childNode) + + return enableHoverStyle + ? createElement(GestureDetector, { gesture }, BaseComponent) + : BaseComponent }) _View.displayName = 'MpxView' diff --git a/packages/webpack-plugin/lib/runtime/components/react/useAnimationHooks.ts b/packages/webpack-plugin/lib/runtime/components/react/useAnimationHooks.ts index 77d1ac050..f7a1f3e25 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/useAnimationHooks.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/useAnimationHooks.ts @@ -13,6 +13,7 @@ import { WithTimingConfig, AnimationCallback } from 'react-native-reanimated' +import { error } from '@mpxjs/utils' import { ExtendedViewStyle } from './types/common' import type { _ViewProps } from './mpx-view' @@ -166,8 +167,17 @@ const formatStyle = (style: ExtendedViewStyle): ExtendedViewStyle => { }) } -export default function useAnimationHooks (props: _ViewProps) { - const { style = {}, animation } = props +export default function useAnimationHooks (props: _ViewProps & { enableAnimation?: boolean }) { + const { style = {}, animation, enableAnimation } = props + + const enableStyleAnimation = enableAnimation || !!animation + const enableAnimationRef = useRef(enableStyleAnimation) + if (enableAnimationRef.current !== enableStyleAnimation) { + error('[Mpx runtime error]: animation use should be stable in the component lifecycle, or you can set [enable-animation] with true.') + } + + if (!enableStyleAnimation) return { enableStyleAnimation } + const originalStyle = formatStyle(style) // id 标识 const id = animation?.id || -1 @@ -336,7 +346,8 @@ export default function useAnimationHooks (props: _ViewProps) { }, {} as { [propName: string]: string | number }) } // ** 生成动画样式 - return useAnimatedStyle(() => { + + const animationStyle = useAnimatedStyle(() => { // console.info(`useAnimatedStyle styles=`, originalStyle) return animatedStyleKeys.value.reduce((styles, key) => { // console.info('getAnimationStyles', key, shareValMap[key].value) @@ -354,4 +365,9 @@ export default function useAnimationHooks (props: _ViewProps) { return styles }, {} as ExtendedViewStyle) }) + + return { + enableStyleAnimation, + animationStyle + } } diff --git a/packages/webpack-plugin/lib/runtime/components/react/utils.tsx b/packages/webpack-plugin/lib/runtime/components/react/utils.tsx index a00bb771e..0efe89a63 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/utils.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/utils.tsx @@ -1,11 +1,13 @@ import { useEffect, useCallback, useMemo, useRef, ReactNode, ReactElement, isValidElement, useContext, useState, Dispatch, SetStateAction, Children, cloneElement } from 'react' import { LayoutChangeEvent, TextStyle, ImageProps, Image } from 'react-native' import { isObject, isFunction, isNumber, hasOwn, diffAndCloneA, error, warn, getFocusedNavigation } from '@mpxjs/utils' -import { VarContext } from './context' +import { VarContext, ScrollViewContext } from './context' import { ExpressionParser, parseFunc, ReplaceSource } from './parser' import { initialWindowMetrics } from 'react-native-safe-area-context' import FastImage, { FastImageProps } from '@d11/react-native-fast-image' -import type { AnyFunc, ExtendedFunctionComponent } from './types/common' +import type { AnyFunc, ExtendedFunctionComponent, ExtendedViewStyle } from './types/common' +import { runOnJS } from 'react-native-reanimated' +import { Gesture } from 'react-native-gesture-handler' export const TEXT_STYLE_REGEX = /color|font.*|text.*|letterSpacing|lineHeight|includeFontPadding|writingDirection/ export const PERCENT_REGEX = /^\s*-?\d+(\.\d+)?%\s*$/ @@ -610,3 +612,66 @@ export function pickStyle (styleObj: Record = {}, pickedKeys: Array return acc }, {}) } + +export function useHoverStyle ({ hoverStyle, hoverStartTime, hoverStayTime, disabled } : { hoverStyle?: ExtendedViewStyle, hoverStartTime: number, hoverStayTime: number, disabled?: boolean }) { + const enableHoverStyle = !!hoverStyle + const enableHoverStyleRef = useRef(enableHoverStyle) + if (enableHoverStyleRef.current !== enableHoverStyle) { + throw new Error('[Mpx runtime error]: hover-class use should be stable in the component lifecycle.') + } + + if (!enableHoverStyle) return { enableHoverStyle } + + const gestureRef = useContext(ScrollViewContext).gestureRef + const [isHover, setIsHover] = useState(false) + const dataRef = useRef<{ + startTimer?: ReturnType + stayTimer?: ReturnType + }>({}) + + useEffect(() => { + return () => { + dataRef.current.startTimer && clearTimeout(dataRef.current.startTimer) + dataRef.current.stayTimer && clearTimeout(dataRef.current.stayTimer) + } + }, []) + + const setStartTimer = () => { + dataRef.current.startTimer && clearTimeout(dataRef.current.startTimer) + dataRef.current.startTimer = setTimeout(() => { + setIsHover(true) + }, +hoverStartTime) + } + + const setStayTimer = () => { + dataRef.current.stayTimer && clearTimeout(dataRef.current.stayTimer) + dataRef.current.startTimer && clearTimeout(dataRef.current.startTimer) + dataRef.current.stayTimer = setTimeout(() => { + setIsHover(false) + }, +hoverStayTime) + } + + const gesture = useMemo(() => { + return Gesture.Pan() + .onTouchesDown(() => { + 'worklet' + if (disabled) return + runOnJS(setStartTimer)() + }) + .onTouchesUp(() => { + 'worklet' + if (disabled) return + runOnJS(setStayTimer)() + }) + }, [disabled]) + + if (gestureRef) { + gesture.simultaneousWithExternalGesture(gestureRef) + } + + return { + isHover, + gesture, + enableHoverStyle + } +}