Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: static & ssr #463

Merged
merged 24 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@
"module": "./plugin/index.js",
"default": "./plugin/index.js"
},
"./package.json": "./package.json"
"./package.json": "./package.json",
"./server": {
"types": "./lib/typescript/src/server/index.d.ts",
"module": "./lib/module/server/index",
"default": "./lib/commonjs/server/index",
"react-native": "./src/server"
}
},
"files": [
"src",
Expand Down Expand Up @@ -88,6 +94,7 @@
"husky": "9.1.7",
"jest": "29.7.0",
"metro-react-native-babel-preset": "0.77.0",
"next": "15.1.1",
jpudysz marked this conversation as resolved.
Show resolved Hide resolved
"nitro-codegen": "0.18.2",
"react": "18.3.1",
"react-native": "0.76.3",
Expand Down
6 changes: 6 additions & 0 deletions server/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"main": "../lib/commonjs/server/index",
"module": "../lib/module/server/index",
"react-native": "../src/server/index",
"types": "../lib/typescript/src/server/index"
}
2 changes: 1 addition & 1 deletion src/components/ScopedTheme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const ScopedTheme: React.FunctionComponent<React.PropsWithChildren<ThemeP
const mappedChildren = [
<Apply key={name} name={name} />,
children,
<Apply key='dispose' name={previousScopedTheme} />
<Apply key='dispose' name={previousScopedTheme as keyof UnistylesThemes | undefined} />
]

return (
Expand Down
4 changes: 4 additions & 0 deletions src/components/native/ImageBackground.native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { ImageBackground as NativeImageBackground } from 'react-native'
import { createUnistylesImageBackground } from '../../core'

export const ImageBackground = createUnistylesImageBackground(NativeImageBackground)
56 changes: 54 additions & 2 deletions src/components/native/ImageBackground.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,56 @@
import React from 'react'
import { ImageBackground as NativeImageBackground } from 'react-native'
import { createUnistylesImageBackground } from '../../core'
import { forwardRef } from 'react'
import type { UnistylesValues } from '../../types'
import { getClassName } from '../../core'
import { isServer } from '../../web/utils'
import { UnistylesShadowRegistry } from '../../web'

export const ImageBackground = createUnistylesImageBackground(NativeImageBackground)
type Props = {
style?: UnistylesValues
imageStyle?: UnistylesValues
}

export const ImageBackground = forwardRef<unknown, Props>((props, forwardedRef) => {
let storedRef: NativeImageBackground | null = null
let storedImageRef: NativeImageBackground | null = null
const styleClassNames = getClassName(props.style)
const imageClassNames = getClassName(props.imageStyle)

return (
// @ts-expect-error - RN types are not compatible with RNW styles
<NativeImageBackground
{...props}
style={styleClassNames}
imageStyle={imageClassNames}
ref={isServer() ? undefined : ref => {
if (!ref) {
// @ts-expect-error hidden from TS
UnistylesShadowRegistry.remove(storedRef, styleClassNames?.hash)
}

storedRef = ref
// @ts-expect-error hidden from TS
UnistylesShadowRegistry.add(ref, styleClassNames?.hash)

if (typeof forwardedRef === 'function') {
return forwardedRef(ref)
}

if (forwardedRef) {
forwardedRef.current = ref
}
}}
imageRef={isServer() ? undefined : ref => {
if (!ref) {
// @ts-expect-error hidden from TS
UnistylesShadowRegistry.remove(storedImageRef, imageClassNames?.hash)
}

storedImageRef = ref
// @ts-expect-error hidden from TS
UnistylesShadowRegistry.add(ref, imageClassNames?.hash)
}}
/>
)
})
5 changes: 2 additions & 3 deletions src/components/native/Pressable.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ export const Pressable = forwardRef<View, PressableProps>(({ variants, style, ..
? unistyles
: [unistyles]

// @ts-expect-error - this is hidden from TS
// @ts-expect-error web types are not compatible with RN styles
UnistylesShadowRegistry.add(ref, styles)

storedRef.current = ref

return passForwardedRef(props, ref, forwardedRef)
Expand All @@ -45,7 +44,7 @@ export const Pressable = forwardRef<View, PressableProps>(({ variants, style, ..
UnistylesShadowRegistry.selectVariants(variants)
}

// @ts-expect-error - this is hidden from TS
// @ts-expect-error hidden from TS
UnistylesShadowRegistry.remove(storedRef.current)
// @ts-expect-error - this is hidden from TS
UnistylesShadowRegistry.add(storedRef.current, styles)
Expand Down
106 changes: 38 additions & 68 deletions src/components/native/Pressable.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import React, { forwardRef, useRef } from 'react'
import React, { forwardRef } from 'react'
import { Pressable as NativePressableReactNative } from 'react-native'
import type { PressableProps as Props, View, ViewStyle } from 'react-native'
import type { PressableProps as Props, View } from 'react-native'
import { UnistylesShadowRegistry } from '../../specs'
import type { UnistylesValues } from '../../types'
import { getClassName } from '../../core'
import { isServer } from '../../web/utils'

type Variants = Record<string, string | boolean | undefined>
type WebPressableState = {
Expand All @@ -10,88 +13,55 @@ type WebPressableState = {
focused: boolean
}

type WebPressableStyle = ((state: WebPressableState) => ViewStyle) | ViewStyle
type WebPressableStyle = ((state: WebPressableState) => UnistylesValues) | UnistylesValues

type PressableProps = Props & {
variants?: Variants
style?: WebPressableStyle,
}

const initialState: WebPressableState = {
pressed: false,
hovered: false,
focused: false
}

type UpdateStylesProps = {
ref: View | null,
style: WebPressableStyle,
variants?: Variants,
state: WebPressableState
scopedTheme?: string
}

const extractStyleResult = (style: any) => {
return typeof style === 'function'
? [style()]
: Array.isArray(style)
? style.map(style => typeof style === 'function' ? style() : style)
: [style]
}

const updateStyles = ({ ref, style, state, scopedTheme, variants }: UpdateStylesProps) => {
const styleResult = typeof style === 'function'
? style(state)
: style
const extractedResult = extractStyleResult(styleResult)
const previousScopedTheme = UnistylesShadowRegistry.getScopedTheme()
const previousVariants = UnistylesShadowRegistry.getVariants()

UnistylesShadowRegistry.selectVariants(variants as unknown as Variants)
UnistylesShadowRegistry.setScopedTheme(scopedTheme as any)

UnistylesShadowRegistry.add(ref, extractedResult)

UnistylesShadowRegistry.setScopedTheme(previousScopedTheme)
UnistylesShadowRegistry.selectVariants(previousVariants as unknown as Variants)
}

export const Pressable = forwardRef<View, PressableProps>(({ style, ...props }, passedRef) => {
const storedRef = useRef<View | null>(null)
export const Pressable = forwardRef<View, PressableProps>(({ style, ...props }, forwardedRef) => {
const scopedTheme = UnistylesShadowRegistry.getScopedTheme()
const variants = UnistylesShadowRegistry.getVariants()
let storedRef: HTMLElement | null = null
let classNames: ReturnType<typeof getClassName> | undefined = undefined

return (
<NativePressableReactNative
{...props}
style={state => {
if (!storedRef.current) {
return {}
}
ref={isServer() ? undefined : ref => {
storedRef = ref as unknown as HTMLElement
// @ts-expect-error hidden from TS
UnistylesShadowRegistry.add(storedRef, classNames?.hash)

updateStyles({
ref: storedRef.current,
style: style as WebPressableStyle,
variants,
scopedTheme,
state: state as WebPressableState
})
if (typeof forwardedRef === 'function') {
return forwardedRef(ref)
}

return {}
if (forwardedRef) {
forwardedRef.current = ref
}
}}
ref={ref => {
storedRef.current = ref
updateStyles({
ref,
style: style as WebPressableStyle,
variants,
scopedTheme,
state: initialState
})
style={state => {
const styleResult = typeof style === 'function'
? style(state as WebPressableState)
: style
const previousScopedTheme = UnistylesShadowRegistry.getScopedTheme()
const previousVariants = UnistylesShadowRegistry.getVariants()

if (typeof passedRef === 'object' && passedRef !== null) {
passedRef.current = ref
}
UnistylesShadowRegistry.selectVariants(variants)
UnistylesShadowRegistry.setScopedTheme(scopedTheme)

// @ts-expect-error hidden from TS
UnistylesShadowRegistry.remove(storedRef, classNames?.hash)
classNames = getClassName(styleResult as UnistylesValues)
// @ts-expect-error hidden from TS
UnistylesShadowRegistry.add(storedRef, classNames?.hash)

UnistylesShadowRegistry.selectVariants(previousVariants)
UnistylesShadowRegistry.setScopedTheme(previousScopedTheme)

return classNames as any
}}
/>
)
Expand Down
9 changes: 9 additions & 0 deletions src/core/createUnistylesElement.native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react'
import { passForwardedRef } from './passForwardRef'

export const createUnistylesElement = (Component: any) => React.forwardRef((props, forwardedRef) => (
<Component
{...props}
ref={(ref: unknown) => passForwardedRef(props, ref, forwardedRef)}
/>
))
46 changes: 38 additions & 8 deletions src/core/createUnistylesElement.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,39 @@
import React from 'react'
import { passForwardedRef } from './passForwardRef'

export const createUnistylesElement = (Component: any) => React.forwardRef((props, forwardedRef) => (
<Component
{...props}
ref={(ref: unknown) => passForwardedRef(props, ref, forwardedRef)}
/>
))
import type { UnistylesValues } from '../types'
import { getClassName } from './getClassname'
import { isServer } from '../web/utils'
import { UnistylesShadowRegistry } from '../web'

type ComponentProps = {
style?: UnistylesValues | Array<UnistylesValues>
}

export const createUnistylesElement = (Component: any) => React.forwardRef<unknown, ComponentProps>((props, forwardedRef) => {
let storedRef: HTMLElement | null = null
const classNames = getClassName(props.style)

return (
<Component
{...props}
style={classNames}
ref={isServer() ? undefined : (ref: HTMLElement | null) => {
if (!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
}
}}
/>
)
})
4 changes: 2 additions & 2 deletions src/core/createUnistylesImageBackground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ export const createUnistylesImageBackground = (Component: typeof ImageBackground
? props.imageStyle
: [props.imageStyle]

// @ts-expect-error - This is hidden from TS
// @ts-expect-error web types are not compatible with RN styles
UnistylesShadowRegistry.add(ref, style)

return () => {
// @ts-expect-error - This is hidden from TS
// @ts-expect-error hidden from TS
UnistylesShadowRegistry.remove(ref)
}
}}
Expand Down
17 changes: 17 additions & 0 deletions src/core/getClassname.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { UnistylesValues } from '../types';
import { deepMergeObjects } from '../utils';
import { UnistylesShadowRegistry } from '../web';

export const getClassName = (unistyle: UnistylesValues | undefined | Array<UnistylesValues>) => {
if (!unistyle) {
return undefined
}

const style = Array.isArray(unistyle)
? deepMergeObjects(...unistyle)
: unistyle
// @ts-expect-error hidden from TS
const { hash, injectedClassName } = UnistylesShadowRegistry.addStyles(style)

return hash ? { $$css: true, hash, injectedClassName } : undefined
}
1 change: 1 addition & 0 deletions src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export { createUnistylesElement } from './createUnistylesElement'
export { createUnistylesImageBackground } from './createUnistylesImageBackground'
export { withUnistyles } from './withUnistyles'
export { passForwardedRef } from './passForwardRef'
export { getClassName } from './getClassname'
3 changes: 2 additions & 1 deletion src/core/passForwardRef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ export const passForwardedRef = <T>(
}
const forwardedRefReturnFn = passForwardedRef()

// @ts-expect-error hidden from TS
UnistylesShadowRegistry.add(ref, props.style)

return () => {
// @ts-expect-error - This is hidden from TS
// @ts-expect-error hidden from TS
UnistylesShadowRegistry.remove(ref)
forwardedRefReturnFn?.()
}
Expand Down
Loading
Loading