Skip to content

Commit

Permalink
Merge pull request #1662 from didi/feat-layout-hook
Browse files Browse the repository at this point in the history
feat: rn layout hook
  • Loading branch information
hiyuki authored Oct 18, 2024
2 parents 120abe7 + 1633c25 commit 59a1a05
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export const getCustomEvent = (
const useInnerProps = (
props: Props = {},
additionalProps: AdditionalProps = {},
removeProps: RemoveProps = [],
userRemoveProps: RemoveProps = [],
rawConfig?: UseInnerPropsConfig
) => {
const ref = useRef<InnerRef>({
Expand All @@ -130,6 +130,17 @@ const useInnerProps = (
const propsRef = useRef<Record<string, any>>({})
const eventConfig: { [key: string]: string[] } = {}
const config = rawConfig || { layoutRef: { current: {} }, disableTouch: false, disableTap: false }
const removeProps = [
'children',
'enable-background',
'enable-offset',
'enable-var',
'external-var-context',
'parent-font-size',
'parent-width',
'parent-height',
...userRemoveProps
]

propsRef.current = { ...props, ...additionalProps }

Expand Down
58 changes: 43 additions & 15 deletions packages/webpack-plugin/lib/runtime/components/react/mpx-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,21 @@
* ✔ bindreset
*/

import { View, LayoutChangeEvent } from 'react-native'
import { View } from 'react-native'
import { JSX, useRef, forwardRef, ReactNode } from 'react'
import useNodesRef, { HandlerRef } from './useNodesRef'
import useInnerProps, { getCustomEvent } from './getInnerListeners'
import { FormContext } from './context'

import { useTransformStyle, splitProps, splitStyle, useLayout, wrapChildren } from './utils'
interface FormProps {
style?: Record<string, any>;
children: ReactNode;
'enable-offset'?: boolean;
'enable-var'?: boolean
'external-var-context'?: Record<string, any>;
'parent-font-size'?: number;
'parent-width'?: number;
'parent-height'?: number;
bindsubmit?: (evt: {
detail: {
value: any;
Expand All @@ -22,18 +28,32 @@ interface FormProps {
bindreset?: () => void;
}

const _Form = forwardRef<HandlerRef<View, FormProps>, FormProps>((props: FormProps, ref): JSX.Element => {
const { children, style } = props
const layoutRef = useRef({})
const _Form = forwardRef<HandlerRef<View, FormProps>, FormProps>((fromProps: FormProps, ref): JSX.Element => {
const { textProps, innerProps: props = {} } = splitProps(fromProps)
const formValuesMap = useRef(new Map()).current
const {
style,
'enable-var': enableVar,
'external-var-context': externalVarContext,
'parent-font-size': parentFontSize,
'parent-width': parentWidth,
'parent-height': parentHeight
} = props

const {
hasSelfPercent,
normalStyle,
hasVarDec,
varContextRef,
setWidth,
setHeight
} = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight })

const { textStyle, innerStyle } = splitStyle(normalStyle)

const { nodeRef: formRef } = useNodesRef(props, ref)

const onLayout = (e: LayoutChangeEvent) => {
formRef.current?.measure((x: number, y: number, width: number, height: number, offsetLeft: number, offsetTop: number) => {
layoutRef.current = { x, y, width, height, offsetLeft, offsetTop }
})
}
const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: formRef })

const submit = () => {
const { bindsubmit } = props
Expand Down Expand Up @@ -63,12 +83,10 @@ const _Form = forwardRef<HandlerRef<View, FormProps>, FormProps>((props: FormPro
}

const innerProps = useInnerProps(props, {
style: { ...innerStyle, ...layoutStyle },
ref: formRef,
style,
onLayout
...layoutProps
}, [
'children',
'style',
'bindsubmit',
'bindreset'
], { layoutRef })
Expand All @@ -78,7 +96,17 @@ const _Form = forwardRef<HandlerRef<View, FormProps>, FormProps>((props: FormPro
{...innerProps}
>
<FormContext.Provider value={{ formValuesMap, submit, reset }}>
{children}
{
wrapChildren(
props,
{
hasVarDec,
varContext: varContextRef.current,
textStyle,
textProps
}
)
}
</FormContext.Provider>
</View>
)
Expand Down
16 changes: 4 additions & 12 deletions packages/webpack-plugin/lib/runtime/components/react/mpx-text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,25 +65,17 @@ const _Text = forwardRef<HandlerRef<Text, _TextProps>, _TextProps>((props, ref):
const { nodeRef } = useNodesRef<Text, _TextProps>(props, ref)

const innerProps = useInnerProps(props, {
ref: nodeRef
ref: nodeRef,
style: normalStyle,
selectable: !!selectable || !!userSelect
}, [
'style',
'children',
'selectable',
'user-select',
'enable-var',
'external-var-context',
'parent-font-size',
'parent-width',
'parent-height'
'user-select'
], {
layoutRef
})

return (
<Text
style={normalStyle}
selectable={!!selectable || !!userSelect}
{...innerProps}
>
{
Expand Down
13 changes: 2 additions & 11 deletions packages/webpack-plugin/lib/runtime/components/react/mpx-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -539,33 +539,24 @@ const _View = forwardRef<HandlerRef<View, _ViewProps>, _ViewProps>((props, ref):

const innerProps = useInnerProps(props, {
ref: nodeRef,
style: innerStyle,
...needLayout ? { onLayout } : null,
...(hoverStyle && {
bindtouchstart: onTouchStart,
bindtouchend: onTouchEnd
})
}, [
'style',
'children',
'hover-start-time',
'hover-stay-time',
'hover-style',
'hover-class',
'enable-offset',
'enable-background-image',
'enable-var',
'external-var-context',
'parent-font-size',
'parent-width',
'parent-height'
'hover-class'
], {
layoutRef
})

return (
<View
{...innerProps}
style={innerStyle}
>
{
wrapChildren(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect, useRef, ReactNode, ReactElement, FunctionComponent, isValidElement, useContext, useState } from 'react'
import { Dimensions, StyleSheet } from 'react-native'
import { useEffect, useRef, ReactNode, ReactElement, FunctionComponent, isValidElement, useContext, useState, Dispatch, SetStateAction, Children, cloneElement } from 'react'
import { Dimensions, StyleSheet, LayoutChangeEvent, TextStyle } from 'react-native'
import { isObject, hasOwn, diffAndCloneA, error, warn } from '@mpxjs/utils'
import { VarContext } from './context'
import { ExpressionParser, parseFunc, ReplaceSource } from './parser'
Expand All @@ -10,6 +10,9 @@ export const URL_REGEX = /^\s*url\(["']?(.*?)["']?\)\s*$/
export const BACKGROUND_REGEX = /^background(Image|Size|Repeat|Position)$/
export const TEXT_PROPS_REGEX = /ellipsizeMode|numberOfLines/
export const DEFAULT_FONT_SIZE = 16
export const DEFAULT_UNLAY_STYLE = {
opacity: 0
}

export const throwReactWarning = (message: string) => {
setTimeout(() => {
Expand Down Expand Up @@ -426,3 +429,65 @@ export function splitProps<T extends Record<string, any>> (props: T) {
}
})
}

interface LayoutConfig {
props: Record<string, any>
hasSelfPercent: boolean
setWidth: Dispatch<SetStateAction<number>>
setHeight: Dispatch<SetStateAction<number>>
onLayout?: (event?: LayoutChangeEvent) => void
nodeRef: React.RefObject<any>
}
export const useLayout = ({ props, hasSelfPercent, setWidth, setHeight, onLayout, nodeRef }:LayoutConfig) => {
const layoutRef = useRef({})
const hasLayoutRef = useRef(false)
const layoutStyle: Record<string, any> = hasLayoutRef.current ? {} : DEFAULT_UNLAY_STYLE
const layoutProps: Record<string, any> = {}
const enableOffset = props['enable-offset']
if (hasSelfPercent || onLayout || enableOffset) {
layoutProps.onLayout = (e: LayoutChangeEvent) => {
hasLayoutRef.current = true
if (hasSelfPercent) {
const { width, height } = e?.nativeEvent?.layout || {}
setWidth(width || 0)
setHeight(height || 0)
}
if (enableOffset) {
nodeRef.current?.measure((x: number, y: number, width: number, height: number, offsetLeft: number, offsetTop: number) => {
layoutRef.current = { x, y, width, height, offsetLeft, offsetTop }
})
}
onLayout && onLayout(e)
props.onLayout && props.onLayout(e)
}
}
return {
layoutRef,
layoutStyle,
layoutProps
}
}

export interface WrapChildrenConfig {
hasVarDec: boolean
varContext?: Record<string, any>
textStyle?: TextStyle
textProps?: Record<string, any>
}

export function wrapChildren (props: Record<string, any> = {}, { hasVarDec, varContext, textStyle, textProps }: WrapChildrenConfig) {
let { children } = props
if (textStyle || textProps) {
children = Children.map(children, (child) => {
if (isText(child)) {
const style = { ...textStyle, ...child.props.style }
return cloneElement(child, { ...textProps, style })
}
return child
})
}
if (hasVarDec && varContext) {
children = <VarContext.Provider value={varContext} key='varContextWrap'>{children}</VarContext.Provider>
}
return children
}

0 comments on commit 59a1a05

Please sign in to comment.