Skip to content

Commit

Permalink
Merge pull request #463 from jpudysz/feature/static-ssr
Browse files Browse the repository at this point in the history
feat: static & ssr
  • Loading branch information
jpudysz authored Dec 20, 2024
2 parents 0fadf2d + 63e7410 commit 7b8430b
Show file tree
Hide file tree
Showing 36 changed files with 978 additions and 488 deletions.
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",
"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

0 comments on commit 7b8430b

Please sign in to comment.