Skip to content

Commit

Permalink
fix(*): fix hover style.
Browse files Browse the repository at this point in the history
  • Loading branch information
shangqunfeng committed Dec 23, 2024
2 parents 0034923 + e0b7c4d commit 939709d
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 83 deletions.
1 change: 1 addition & 0 deletions packages/core/src/platform/createApp.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ export default function createApp (option, config = {}) {
// 7.x替换headerBackTitleVisible
// headerBackButtonDisplayMode: 'minimal',
headerBackTitleVisible: false,
// 安卓上会出现初始化时闪现导航条的问题
headerShown: false
}
if (headerBackImageProps) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 })
Expand All @@ -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
}
Expand All @@ -36,6 +38,11 @@ module.exports = function ({ print }) {
{
test: /^(htouchmove|vtouchmove)$/,
ali: aliEventLog
},
{
test: /^(bindscale)$/,
ios: iosEventLog,
android: androidEventLog
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ export interface IntersectionObserver {
}
}

export interface ScrollViewContextValue {
gestureRef: React.RefObject<any> | null
}

export const MovableAreaContext = createContext({ width: 0, height: 0 })

export const FormContext = createContext<FormContextValue | null>(null)
Expand All @@ -52,3 +56,5 @@ export const IntersectionObserverContext = createContext<IntersectionObserver |
export const RouteContext = createContext<number | null>(null)

export const KeyboardAvoidContext = createContext<KeyboardAvoidContextValue | null>(null)

export const ScrollViewContext = createContext<ScrollViewContextValue>({ gestureRef: null })
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* ✘ scale-min
* ✘ scale-max
* ✘ scale-value
* animation
* animation
* ✔ bindchange
* ✘ bindscale
* ✔ htouchmove
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -91,28 +91,22 @@ const _RichText = forwardRef<HandlerRef<View, _RichTextProps>, _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 (
<View
{...innerProps}
>
<WebView
source={{ html: generateHTML(html) }}
onMessage={(event: WebViewMessageEvent) => {
setWebViewHeight(+event.nativeEvent.data)
}}
>
</WebView>
</View>
return createElement(View, innerProps,
createElement(WebView, {
source: { html: generateHTML(html) },
onMessage: (event: WebViewMessageEvent) => {
setWebViewHeight(+event.nativeEvent.data)
}
})
)
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -194,6 +194,12 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
gestureRef: scrollViewRef
})

const contextValue = useMemo(() => {
return {
gestureRef: scrollViewRef
}
}, [])

const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout })

if (scrollX && scrollY) {
Expand Down Expand Up @@ -507,14 +513,17 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, 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
}
)
)
)
})
Expand Down
62 changes: 11 additions & 51 deletions packages/webpack-plugin/lib/runtime/components/react/mpx-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -683,8 +684,6 @@ const _View = forwardRef<HandlerRef<View, _ViewProps>, _ViewProps>((viewProps, r
animation
} = props

const [isHover, setIsHover] = useState(false)

// 默认样式
const defaultStyle: ExtendedViewStyle = style.display === 'flex'
? {
Expand All @@ -695,6 +694,8 @@ const _View = forwardRef<HandlerRef<View, _ViewProps>, _ViewProps>((viewProps, r
}
: {}

const { isHover, enableHoverStyle, gesture } = useHoverStyle({ hoverStyle, hoverStartTime, hoverStayTime })

const styleObj: ExtendedViewStyle = extendObject({}, defaultStyle, style, isHover ? hoverStyle as ExtendedViewStyle : {})

const {
Expand Down Expand Up @@ -725,45 +726,6 @@ const _View = forwardRef<HandlerRef<View, _ViewProps>, _ViewProps>((viewProps, r
style: normalStyle
})

const dataRef = useRef<{
startTimer?: ReturnType<typeof setTimeout>
stayTimer?: ReturnType<typeof setTimeout>
}>({})

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<TouchEvent>) {
const { bindtouchstart } = props
bindtouchstart && bindtouchstart(e)
setStartTimer()
}

function onTouchEnd (e: NativeSyntheticEvent<TouchEvent>) {
const { bindtouchend } = props
bindtouchend && bindtouchend(e)
setStayTimer()
}

const {
layoutRef,
layoutStyle,
Expand All @@ -789,13 +751,7 @@ const _View = forwardRef<HandlerRef<View, _ViewProps>, _ViewProps>((viewProps, r
ref: nodeRef,
style: finalStyle
},
layoutProps,
hoverStyle
? {
bindtouchstart: onTouchStart,
bindtouchend: onTouchEnd
}
: {}
layoutProps
), [
'hover-start-time',
'hover-stay-time',
Expand All @@ -816,9 +772,13 @@ const _View = forwardRef<HandlerRef<View, _ViewProps>, _ViewProps>((viewProps, r
enableFastImage
})

return enableAnimation
? createElement(Animated.View, innerProps, childNode)
const BaseComponent = enableAnimation
? createElement(Animated.View, extendObject({}, innerProps, { style: finalStyle }), childNode)
: createElement(View, innerProps, childNode)

return enableHoverStyle
? createElement(GestureDetector, { gesture }, BaseComponent)
: BaseComponent
})

_View.displayName = 'MpxView'
Expand Down
69 changes: 67 additions & 2 deletions packages/webpack-plugin/lib/runtime/components/react/utils.tsx
Original file line number Diff line number Diff line change
@@ -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*$/
Expand Down Expand Up @@ -611,3 +613,66 @@ export function pickStyle (styleObj: Record<string, any> = {}, 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<typeof setTimeout>
stayTimer?: ReturnType<typeof setTimeout>
}>({})

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
}
}

0 comments on commit 939709d

Please sign in to comment.