From f7a9cf0263f0567e29bea08fb38410d02c6dd84e Mon Sep 17 00:00:00 2001 From: Hubert Bieszczad Date: Wed, 29 Jan 2025 10:03:03 +0100 Subject: [PATCH] feat: copy native components properties --- src/components/native/ImageBackground.tsx | 5 +- src/core/createUnistylesElement.native.tsx | 51 ++++++++------- src/core/createUnistylesElement.tsx | 63 +++++++++--------- src/core/createUnistylesImageBackground.tsx | 71 +++++++++++---------- src/utils.ts | 14 ++++ 5 files changed, 118 insertions(+), 86 deletions(-) diff --git a/src/components/native/ImageBackground.tsx b/src/components/native/ImageBackground.tsx index 45228d3c..7a430a10 100644 --- a/src/components/native/ImageBackground.tsx +++ b/src/components/native/ImageBackground.tsx @@ -5,13 +5,14 @@ import type { UnistylesValues } from '../../types' import { getClassName } from '../../core' import { isServer } from '../../web/utils' import { UnistylesShadowRegistry } from '../../web' +import { copyComponentProperties } from '../../utils' type Props = { style?: UnistylesValues imageStyle?: UnistylesValues } -export const ImageBackground = forwardRef((props, forwardedRef) => { +const UnistylesImageBackground = forwardRef((props, forwardedRef) => { let storedRef: NativeImageBackground | null = null let storedImageRef: NativeImageBackground | null = null const styleClassNames = getClassName(props.style) @@ -54,3 +55,5 @@ export const ImageBackground = forwardRef((props, forwardedRef) /> ) }) + +export const ImageBackground = copyComponentProperties(NativeImageBackground, UnistylesImageBackground) diff --git a/src/core/createUnistylesElement.native.tsx b/src/core/createUnistylesElement.native.tsx index ea43250d..9ea86a3f 100644 --- a/src/core/createUnistylesElement.native.tsx +++ b/src/core/createUnistylesElement.native.tsx @@ -2,6 +2,7 @@ import React, { useLayoutEffect, useRef } from 'react' import { UnistylesShadowRegistry } from '../specs' import { passForwardedRef } from './passForwardRef' import { maybeWarnAboutMultipleUnistyles } from './warn' +import { copyComponentProperties } from '../utils' const getNativeRef = (Component: any, ref: any) => { switch (Component.name) { @@ -16,31 +17,35 @@ const getNativeRef = (Component: any, ref: any) => { } } -export const createUnistylesElement = (Component: any) => React.forwardRef((props, forwardedRef) => { - const storedRef = useRef(null) +export const createUnistylesElement = (Component: any) => { + const UnistylesComponent = React.forwardRef((props, forwardedRef) => { + const storedRef = useRef(null) - useLayoutEffect(() => { - return () => { - if (storedRef.current) { - // @ts-ignore - UnistylesShadowRegistry.remove(storedRef.current) + useLayoutEffect(() => { + return () => { + if (storedRef.current) { + // @ts-ignore + UnistylesShadowRegistry.remove(storedRef.current) + } } - } - }, []) + }, []) - return ( - { - if (ref) { - storedRef.current = getNativeRef(Component, ref) - } + return ( + { + if (ref) { + storedRef.current = getNativeRef(Component, ref) + } - passForwardedRef(props, ref, forwardedRef) + passForwardedRef(props, ref, forwardedRef) - // @ts-ignore we don't know the type of the component - maybeWarnAboutMultipleUnistyles(props.style, Component.displayName) - }} - /> - ) -}) + // @ts-ignore we don't know the type of the component + maybeWarnAboutMultipleUnistyles(props.style, Component.displayName) + }} + /> + ) + }) + + return copyComponentProperties(Component, UnistylesComponent) +} diff --git a/src/core/createUnistylesElement.tsx b/src/core/createUnistylesElement.tsx index 71235a29..e5e236ba 100644 --- a/src/core/createUnistylesElement.tsx +++ b/src/core/createUnistylesElement.tsx @@ -4,40 +4,45 @@ import { getClassName } from './getClassname' import { isServer } from '../web/utils' import { UnistylesShadowRegistry } from '../web' import { maybeWarnAboutMultipleUnistyles } from './warn' +import { copyComponentProperties } from '../utils' type ComponentProps = { style?: UnistylesValues | Array } -export const createUnistylesElement = (Component: any) => React.forwardRef((props, forwardedRef) => { - let storedRef: HTMLElement | null = null - const classNames = getClassName(props.style) +export const createUnistylesElement = (Component: any) => { + const UnistylesComponent = React.forwardRef((props, forwardedRef) => { + let storedRef: HTMLElement | null = null + const classNames = getClassName(props.style) - return ( - { - // @ts-ignore we don't know the type of the component - maybeWarnAboutMultipleUnistyles(props.style, Component.displayName) + return ( + { + // @ts-ignore we don't know the type of the component + maybeWarnAboutMultipleUnistyles(props.style, Component.displayName) - if (!ref) { + if (!ref) { + // @ts-expect-error hidden from TS + UnistylesShadowRegistry.remove(storedRef, classNames?.hash) + } + + storedRef = ref // @ts-expect-error hidden from TS - UnistylesShadowRegistry.remove(storedRef, classNames?.hash) - } - - storedRef = ref - // @ts-expect-error hidden from TS - UnistylesShadowRegistry.add(ref, classNames?.hash) - - if (typeof forwardedRef === 'function') { - return forwardedRef(ref) - } - - if (forwardedRef) { - forwardedRef.current = ref - } - }} - /> - ) -}) + UnistylesShadowRegistry.add(ref, classNames?.hash) + + if (typeof forwardedRef === 'function') { + return forwardedRef(ref) + } + + if (forwardedRef) { + forwardedRef.current = ref + } + }} + /> + ) + }) + + return copyComponentProperties(Component, UnistylesComponent) +} diff --git a/src/core/createUnistylesImageBackground.tsx b/src/core/createUnistylesImageBackground.tsx index 10978595..5f864e84 100644 --- a/src/core/createUnistylesImageBackground.tsx +++ b/src/core/createUnistylesImageBackground.tsx @@ -3,43 +3,48 @@ import type { Image, ImageBackground, ImageBackgroundProps } from 'react-native' import { UnistylesShadowRegistry } from '../specs' import { passForwardedRef } from './passForwardRef' import { maybeWarnAboutMultipleUnistyles } from './warn' +import { copyComponentProperties } from '../utils' -export const createUnistylesImageBackground = (Component: typeof ImageBackground) => React.forwardRef((props, forwardedRef) => { - const storedImageRef = useRef(null) +export const createUnistylesImageBackground = (Component: typeof ImageBackground) => { + const UnistylesImageBackground = React.forwardRef((props, forwardedRef) => { + const storedImageRef = useRef(null) - useLayoutEffect(() => { - return () => { - if (storedImageRef.current) { - // @ts-ignore - UnistylesShadowRegistry.remove(storedImageRef.current) + useLayoutEffect(() => { + return () => { + if (storedImageRef.current) { + // @ts-ignore + UnistylesShadowRegistry.remove(storedImageRef.current) + } } - } - }, []) + }, []) - // @ts-expect-error we don't know the type of the component - maybeWarnAboutMultipleUnistyles(props.style, 'ImageBackground') - // @ts-ignore we don't know the type of the component - maybeWarnAboutMultipleUnistyles(props.imageStyle, 'ImageBackground') + // @ts-expect-error we don't know the type of the component + maybeWarnAboutMultipleUnistyles(props.style, 'ImageBackground') + // @ts-ignore we don't know the type of the component + maybeWarnAboutMultipleUnistyles(props.imageStyle, 'ImageBackground') - return ( - { - passForwardedRef(props, ref, forwardedRef) + return ( + { + passForwardedRef(props, ref, forwardedRef) - return () => { - // @ts-ignore - UnistylesShadowRegistry.remove(ref) - } - }} - imageRef={ref => { - if (ref) { - storedImageRef.current = ref - } + return () => { + // @ts-ignore + UnistylesShadowRegistry.remove(ref) + } + }} + imageRef={ref => { + if (ref) { + storedImageRef.current = ref + } + + // @ts-expect-error web types are not compatible with RN styles + UnistylesShadowRegistry.add(ref, props.imageStyle) + }} + /> + ) + }) - // @ts-expect-error web types are not compatible with RN styles - UnistylesShadowRegistry.add(ref, props.imageStyle) - }} - /> - ) -}) + return copyComponentProperties(Component, UnistylesImageBackground) +} diff --git a/src/utils.ts b/src/utils.ts index fde9dd34..9b8ac546 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -24,3 +24,17 @@ export const deepMergeObjects = >(...sources: return target } + +export const copyComponentProperties = (Component: any, UnistylesComponent: any) => { + Object.entries(Object.getOwnPropertyDescriptors(Component)).forEach(([key, propertyDescriptor]) => { + // Filter out the keys we don't want to copy + if (['$$typeof', 'render'].includes(key)) { + return + } + + // @ts-expect-error Copy extra component properties - example: Image.getSize, Image.displayName + UnistylesComponent[key] = propertyDescriptor.value ?? propertyDescriptor.get() + }) + + return UnistylesComponent +}