Skip to content

Commit

Permalink
Merge pull request #498 from edkimmel/main
Browse files Browse the repository at this point in the history
Web fixes and server support for 3.0.0
  • Loading branch information
Brentlok authored Jan 20, 2025
2 parents 3e17fcc + ed568ca commit 58ef985
Show file tree
Hide file tree
Showing 12 changed files with 84 additions and 231 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@
"husky": "9.1.7",
"jest": "29.7.0",
"metro-react-native-babel-preset": "0.77.0",
"next": "15.1.4",
"nitro-codegen": "0.21.0",
"react": "18.3.1",
"react-native": "0.76.6",
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export type { UnistylesThemes, UnistylesBreakpoints } from './global'
export { withUnistyles, useUnistyles } from './core'
export type { UnistylesVariants } from './types'
export { Display, Hide, ScopedTheme } from './components'
export { useServerUnistyles, hydrateServerUnistyles, getServerUnistyles, resetServerUnistyles } from './server'
20 changes: 20 additions & 0 deletions src/server/getServerUnistyles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react'
import { StyleSheet } from 'react-native'
import { error, isServer } from '../web/utils'
import { UnistylesWeb } from '../web'
import { DefaultServerUnistylesSettings, type ServerUnistylesSettings } from './types'

export const getServerUnistyles = ({ includeRNWStyles = true }: ServerUnistylesSettings = DefaultServerUnistylesSettings) => {
if (!isServer()) {
throw error('Server styles should only be read on the server')
}
// @ts-ignore
const rnwStyle: string | null = includeRNWStyles ? (StyleSheet?.getSheet().textContent ?? '') : null
const css = UnistylesWeb.registry.css.getStyles()
const state = UnistylesWeb.registry.css.getState()
return <>
{rnwStyle && <style id='rnw-style'>{rnwStyle}</style>}
<style id='unistyles-web'>{css}</style>
<script id='unistyles-script'>{`window.__UNISTYLES_STATE__ = ${JSON.stringify(state)}`}</script>
</>
}
17 changes: 17 additions & 0 deletions src/server/hydrateServerUnistyles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { error, isServer } from '../web/utils'
import { UnistylesWeb } from '../web'

declare global {
interface Window {
// @ts-ignore
__UNISTYLES_STATE__: ReturnType<typeof UnistylesWeb.registry.css.getState>
}
}

export const hydrateServerUnistyles = () => {
if (isServer()) {
throw error('Server styles should only be hydrated on the client')
}
UnistylesWeb.registry.css.hydrate(window.__UNISTYLES_STATE__)
document.querySelector('#unistyles-script')?.remove()
}
4 changes: 4 additions & 0 deletions src/server/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export { useServerUnistyles } from './useServerUnistyles'
export { getServerUnistyles } from './getServerUnistyles'
export { resetServerUnistyles } from './resetServerUnistyles'
export { hydrateServerUnistyles } from './hydrateServerUnistyles'

9 changes: 9 additions & 0 deletions src/server/resetServerUnistyles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { error, isServer } from '../web/utils'
import { UnistylesWeb } from '../web'

export const resetServerUnistyles = () => {
if (!isServer()) {
throw error('Server styles should only be reset on the server')
}
UnistylesWeb.registry.reset()
}
4 changes: 4 additions & 0 deletions src/server/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type ServerUnistylesSettings = {
includeRNWStyles?: boolean
}
export const DefaultServerUnistylesSettings = {}
50 changes: 15 additions & 35 deletions src/server/useServerUnistyles.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,22 @@
import React, { useRef } from 'react'
import { StyleSheet } from 'react-native'
import { useServerInsertedHTML } from 'next/navigation'
import { UnistylesWeb } from '../web'
import { isServer } from '../web/utils'
import { DefaultServerUnistylesSettings, type ServerUnistylesSettings } from './types'
import { getServerUnistyles } from './getServerUnistyles'
import { resetServerUnistyles } from './resetServerUnistyles'
import { hydrateServerUnistyles } from './hydrateServerUnistyles'

declare global {
interface Window {
// @ts-ignore
__UNISTYLES_STATE__: ReturnType<typeof UnistylesWeb.registry.css.getState>
}
}

export const useServerUnistyles = () => {
export const useServerUnistyles = (settings: ServerUnistylesSettings = DefaultServerUnistylesSettings): React.ReactNode | null => {
const isServerInserted = useRef(false)

useServerInsertedHTML(() => {
if (!isServerInserted.current) {
isServerInserted.current = true

// @ts-ignore
const rnwStyle = StyleSheet?.getSheet().textContent ?? ''
const css = UnistylesWeb.registry.css.getStyles()
const state = UnistylesWeb.registry.css.getState()
UnistylesWeb.registry.reset()

return (
<>
<style id='rnw-style'>{rnwStyle}</style>
<style id='unistyles-web'>{css}</style>
<script id='unistyles-script'>{`window.__UNISTYLES_STATE__ = ${JSON.stringify(state)}`}</script>
</>
)
}

return null
})
if (isServer() && !isServerInserted.current) {
isServerInserted.current = true
const components = getServerUnistyles(settings)
resetServerUnistyles()
return components
}

if (typeof window !== 'undefined') {
UnistylesWeb.registry.css.hydrate(window.__UNISTYLES_STATE__)
document.querySelector('#unistyles-script')?.remove()
if (!isServer()) {
hydrateServerUnistyles()
}
return null
}
6 changes: 4 additions & 2 deletions src/web/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ export class UnistylesState {
return
}

if (!this.hasAdaptiveThemes && this.CSSVars) {
document.querySelector(':root')?.classList.add(this.themeName ?? '')
// Ensure we have a themeName before calling this
// classList.add throws a "SyntaxError" DOMException if one of the arguments is an empty string.
if (!this.hasAdaptiveThemes && this.CSSVars && this.themeName) {
document.querySelector(':root')?.classList.add(this.themeName)
}

this.services.listener.initListeners()
Expand Down
2 changes: 1 addition & 1 deletion src/web/utils/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const reduceObject = <TObj extends Record<string, any>, TReducer>(

export const keyInObject = <T extends Record<string, any>>(obj: T, key: PropertyKey): key is keyof T => key in obj

export const isServer = () => typeof window === 'undefined'
export const isServer = () => typeof window === 'undefined' || typeof document === 'undefined'

export const error = (message: string) => new Error(`Unistyles: ${message}`)

Expand Down
5 changes: 3 additions & 2 deletions src/web/utils/unistyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,19 @@ export const isInDocument = (element: HTMLElement) => document.body.contains(ele

export const getMediaQuery = (query: string, allBreakpoints: Array<string>) => {
if (Object.values(Orientation).includes(query as Orientation)) {
return `(orientation: ${query})`
return `@media (orientation: ${query})`
}

if (isUnistylesMq(query)) {
const { minWidth, maxWidth, minHeight, maxHeight } = parseMq(query)

return [
const queries = [
minWidth ? `(min-width: ${minWidth}px)` : undefined,
maxWidth ? `(max-width: ${maxWidth}px)` : undefined,
minHeight ? `(min-height: ${minHeight}px)` : undefined,
maxHeight ? `(max-height: ${maxHeight}px)` : undefined
].filter(Boolean).join(' and ')
return `@media ${queries}`
}

const breakpointValue = UnistylesWeb.runtime.breakpoints[query as keyof UnistylesBreakpoints] ?? 0
Expand Down
Loading

0 comments on commit 58ef985

Please sign in to comment.