import React, { forwardRef, useEffect, type ComponentType, useRef } from 'react'
import { type UnistyleDependency, UnistylesShadowRegistry } from '../../specs'
import type { UnistylesTheme } from '../../types'
import { deepMergeObjects } from '../../utils'
import { useProxifiedUnistyles } from '../useProxifiedUnistyles'
import { maybeWarnAboutMultipleUnistyles } from '../warn'
import type { Mappings } from './types'
// @ts-expect-error
type GenericComponentProps
= ComponentProps
// @ts-expect-error
type GenericComponentRef = ComponentRef
type UnistylesSecrets = {
uni__getStyles: () => Record,
uni__dependencies: Array
}
type MappedSecrets = {
styles: Record,
dependencies: Array
}
export const withUnistyles = >(Component: TComponent, mappings?: Mappings) => {
type TProps = GenericComponentProps
type PropsWithUnistyles = Partial & {
uniProps?: Mappings
}
const getSecrets = (styleProps: Record = {}): MappedSecrets => {
const styles = Array.isArray(styleProps)
? styleProps.flat()
: [styleProps]
const secrets: Array = styles
.filter(Boolean)
.reduce((acc, style) => {
const unistyleKey = Object
.keys(style)
.find(key => key.startsWith('unistyles_'))
return acc.concat([
unistyleKey
? style[unistyleKey]
: {
uni__getStyles: () => style,
uni__dependencies: [],
}
])
}, [])
return {
styles: secrets.reduce((acc, secret) => Object
.assign(acc, secret.uni__getStyles()), {} as Record),
dependencies: secrets.flatMap(secret => secret.uni__dependencies),
}
}
return forwardRef, PropsWithUnistyles>((props, ref) => {
const narrowedProps = props as PropsWithUnistyles
const NativeComponent = Component as ComponentType
// @ts-ignore we don't know the type of the component
maybeWarnAboutMultipleUnistyles(narrowedProps.style, `withUnistyles(${Component.displayName ?? Component.name ?? 'Unknown'})`)
// @ts-ignore we don't know the type of the component
maybeWarnAboutMultipleUnistyles(narrowedProps.contentContainerStyle, `withUnistyles(${Component.displayName ?? Component.name ?? 'Unknown'})`)
const scopedTheme = useRef(UnistylesShadowRegistry.getScopedTheme() as UnistylesTheme)
const { proxifiedRuntime, proxifiedTheme, addDependencies } = useProxifiedUnistyles(scopedTheme.current)
useEffect(() => {
const styleSecrets = getSecrets(narrowedProps.style)
const contentContainerStyleSecrets = getSecrets(narrowedProps.contentContainerStyle)
addDependencies(Array.from(new Set([...styleSecrets.dependencies, ...contentContainerStyleSecrets.dependencies])))
}, [narrowedProps.style, narrowedProps.contentContainerStyle])
const { key: mappingsKey, ...mappingsProps } = mappings ? mappings(proxifiedTheme, proxifiedRuntime) : {}
const { key: uniPropsKey, ...unistyleProps } = narrowedProps.uniProps ? narrowedProps.uniProps(proxifiedTheme, proxifiedRuntime) : {}
const styleSecrets = getSecrets(narrowedProps.style)
const contentContainerStyleSecrets = getSecrets(narrowedProps.contentContainerStyle)
const finalProps = {
...deepMergeObjects(mappingsProps, unistyleProps, props),
...narrowedProps.style ? {
style: styleSecrets.styles,
} : {},
...narrowedProps.contentContainerStyle ? {
contentContainerStyle: contentContainerStyleSecrets.styles,
} : {},
} as any
return (
)
})
}