react-native-unistyles 3.6.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +4 -9
  2. package/android/build.gradle +42 -0
  3. package/android/src/legacy/java/com/unistyles/NativePlatform+extraWindows.kt +16 -0
  4. package/android/src/main/cxx/NativeUnistylesModule.cpp +5 -11
  5. package/android/src/main/cxx/NativeUnistylesModule.h +0 -4
  6. package/android/src/main/java/com/unistyles/NativePlatform+android.kt +82 -39
  7. package/android/src/main/java/com/unistyles/NativePlatform+insets.kt +73 -0
  8. package/android/src/main/java/com/unistyles/UnistylesModule.kt +2 -13
  9. package/android/src/rn86/java/com/unistyles/NativePlatform+extraWindows.kt +28 -0
  10. package/cxx/common/PerformanceTracing.h +85 -0
  11. package/cxx/converters/BackgroundLayerConverter.cpp +523 -0
  12. package/cxx/converters/BackgroundLayerConverter.h +14 -0
  13. package/cxx/converters/FontVariationSettingsConverter.cpp +104 -0
  14. package/cxx/converters/FontVariationSettingsConverter.h +17 -0
  15. package/cxx/core/UnistyleWrapper.h +16 -1
  16. package/cxx/hybridObjects/HybridShadowRegistry.cpp +8 -0
  17. package/cxx/hybridObjects/HybridStyleSheet.cpp +11 -0
  18. package/cxx/hybridObjects/HybridStyleSheet.h +1 -0
  19. package/cxx/hybridObjects/HybridUnistylesRuntime.cpp +10 -30
  20. package/cxx/hybridObjects/HybridUnistylesRuntime.h +2 -2
  21. package/cxx/parser/Parser.cpp +75 -10
  22. package/cxx/shadowTree/ShadowTreeDiagnostics.cpp +74 -0
  23. package/cxx/shadowTree/ShadowTreeManager.cpp +3 -0
  24. package/lib/commonjs/components/NamedTheme.js +4 -4
  25. package/lib/commonjs/components/NamedTheme.js.map +1 -1
  26. package/lib/commonjs/components/ScopedThemeContext.js +3 -1
  27. package/lib/commonjs/components/ScopedThemeContext.js.map +1 -1
  28. package/lib/commonjs/core/createUnistylesImageBackground.js +6 -2
  29. package/lib/commonjs/core/createUnistylesImageBackground.js.map +1 -1
  30. package/lib/commonjs/core/withUnistyles/withUnistyles.js +10 -7
  31. package/lib/commonjs/core/withUnistyles/withUnistyles.js.map +1 -1
  32. package/lib/commonjs/core/withUnistyles/withUnistyles.native.js +22 -3
  33. package/lib/commonjs/core/withUnistyles/withUnistyles.native.js.map +1 -1
  34. package/lib/commonjs/mocks.js +27 -3
  35. package/lib/commonjs/mocks.js.map +1 -1
  36. package/lib/commonjs/specs/ShadowRegistry/index.js +13 -3
  37. package/lib/commonjs/specs/ShadowRegistry/index.js.map +1 -1
  38. package/lib/commonjs/specs/ShadowRegistry/tracing.js +26 -0
  39. package/lib/commonjs/specs/ShadowRegistry/tracing.js.map +1 -0
  40. package/lib/commonjs/utils.js +52 -1
  41. package/lib/commonjs/utils.js.map +1 -1
  42. package/lib/commonjs/web/convert/index.js +19 -1
  43. package/lib/commonjs/web/convert/index.js.map +1 -1
  44. package/lib/commonjs/web/convert/module.d.ts +3 -0
  45. package/lib/commonjs/web/convert/object/backgroundLayers.js +30 -0
  46. package/lib/commonjs/web/convert/object/backgroundLayers.js.map +1 -0
  47. package/lib/commonjs/web/convert/object/fontVariationSettings.js +12 -0
  48. package/lib/commonjs/web/convert/object/fontVariationSettings.js.map +1 -0
  49. package/lib/commonjs/web/convert/object/index.js +22 -0
  50. package/lib/commonjs/web/convert/object/index.js.map +1 -1
  51. package/lib/commonjs/web/convert/utils.js +7 -1
  52. package/lib/commonjs/web/convert/utils.js.map +1 -1
  53. package/lib/commonjs/web/registry.js +6 -4
  54. package/lib/commonjs/web/registry.js.map +1 -1
  55. package/lib/commonjs/web/runtime.js +9 -6
  56. package/lib/commonjs/web/runtime.js.map +1 -1
  57. package/lib/commonjs/web/utils/unistyle.js +1 -3
  58. package/lib/commonjs/web/utils/unistyle.js.map +1 -1
  59. package/lib/module/components/NamedTheme.js +4 -4
  60. package/lib/module/components/NamedTheme.js.map +1 -1
  61. package/lib/module/components/ScopedThemeContext.js +3 -1
  62. package/lib/module/components/ScopedThemeContext.js.map +1 -1
  63. package/lib/module/core/createUnistylesImageBackground.js +6 -2
  64. package/lib/module/core/createUnistylesImageBackground.js.map +1 -1
  65. package/lib/module/core/withUnistyles/withUnistyles.js +9 -6
  66. package/lib/module/core/withUnistyles/withUnistyles.js.map +1 -1
  67. package/lib/module/core/withUnistyles/withUnistyles.native.js +24 -5
  68. package/lib/module/core/withUnistyles/withUnistyles.native.js.map +1 -1
  69. package/lib/module/mocks.js +27 -3
  70. package/lib/module/mocks.js.map +1 -1
  71. package/lib/module/specs/ShadowRegistry/index.js +13 -3
  72. package/lib/module/specs/ShadowRegistry/index.js.map +1 -1
  73. package/lib/module/specs/ShadowRegistry/tracing.js +21 -0
  74. package/lib/module/specs/ShadowRegistry/tracing.js.map +1 -0
  75. package/lib/module/utils.js +50 -0
  76. package/lib/module/utils.js.map +1 -1
  77. package/lib/module/web/convert/index.js +22 -3
  78. package/lib/module/web/convert/index.js.map +1 -1
  79. package/lib/module/web/convert/module.d.ts +3 -0
  80. package/lib/module/web/convert/object/backgroundLayers.js +25 -0
  81. package/lib/module/web/convert/object/backgroundLayers.js.map +1 -0
  82. package/lib/module/web/convert/object/fontVariationSettings.js +7 -0
  83. package/lib/module/web/convert/object/fontVariationSettings.js.map +1 -0
  84. package/lib/module/web/convert/object/index.js +2 -0
  85. package/lib/module/web/convert/object/index.js.map +1 -1
  86. package/lib/module/web/convert/utils.js +4 -0
  87. package/lib/module/web/convert/utils.js.map +1 -1
  88. package/lib/module/web/registry.js +6 -4
  89. package/lib/module/web/registry.js.map +1 -1
  90. package/lib/module/web/runtime.js +9 -6
  91. package/lib/module/web/runtime.js.map +1 -1
  92. package/lib/module/web/utils/unistyle.js +2 -4
  93. package/lib/module/web/utils/unistyle.js.map +1 -1
  94. package/lib/typescript/src/components/ScopedThemeContext.d.ts +2 -1
  95. package/lib/typescript/src/components/ScopedThemeContext.d.ts.map +1 -1
  96. package/lib/typescript/src/core/createUnistylesImageBackground.d.ts.map +1 -1
  97. package/lib/typescript/src/core/withUnistyles/withUnistyles.d.ts +6 -3
  98. package/lib/typescript/src/core/withUnistyles/withUnistyles.d.ts.map +1 -1
  99. package/lib/typescript/src/core/withUnistyles/withUnistyles.native.d.ts +7 -4
  100. package/lib/typescript/src/core/withUnistyles/withUnistyles.native.d.ts.map +1 -1
  101. package/lib/typescript/src/specs/ShadowRegistry/index.d.ts.map +1 -1
  102. package/lib/typescript/src/specs/ShadowRegistry/tracing.d.ts +2 -0
  103. package/lib/typescript/src/specs/ShadowRegistry/tracing.d.ts.map +1 -0
  104. package/lib/typescript/src/utils.d.ts +1 -0
  105. package/lib/typescript/src/utils.d.ts.map +1 -1
  106. package/lib/typescript/src/web/convert/index.d.ts.map +1 -1
  107. package/lib/typescript/src/web/convert/object/backgroundLayers.d.ts +4 -0
  108. package/lib/typescript/src/web/convert/object/backgroundLayers.d.ts.map +1 -0
  109. package/lib/typescript/src/web/convert/object/fontVariationSettings.d.ts +4 -0
  110. package/lib/typescript/src/web/convert/object/fontVariationSettings.d.ts.map +1 -0
  111. package/lib/typescript/src/web/convert/object/index.d.ts +2 -0
  112. package/lib/typescript/src/web/convert/object/index.d.ts.map +1 -1
  113. package/lib/typescript/src/web/convert/utils.d.ts +2 -0
  114. package/lib/typescript/src/web/convert/utils.d.ts.map +1 -1
  115. package/lib/typescript/src/web/registry.d.ts.map +1 -1
  116. package/lib/typescript/src/web/runtime.d.ts +1 -0
  117. package/lib/typescript/src/web/runtime.d.ts.map +1 -1
  118. package/lib/typescript/src/web/utils/unistyle.d.ts.map +1 -1
  119. package/metro/babel-transformer.js +79 -17
  120. package/package.json +26 -26
  121. package/plugin/index.js +79 -17
  122. package/src/components/NamedTheme.tsx +5 -5
  123. package/src/components/ScopedThemeContext.ts +6 -2
  124. package/src/core/createUnistylesImageBackground.tsx +6 -2
  125. package/src/core/withUnistyles/withUnistyles.native.tsx +28 -6
  126. package/src/core/withUnistyles/withUnistyles.tsx +6 -6
  127. package/src/mocks.ts +14 -5
  128. package/src/specs/ShadowRegistry/index.ts +14 -3
  129. package/src/specs/ShadowRegistry/tracing.ts +23 -0
  130. package/src/utils.ts +51 -0
  131. package/src/web/convert/index.ts +39 -3
  132. package/src/web/convert/object/backgroundLayers.ts +33 -0
  133. package/src/web/convert/object/fontVariationSettings.ts +7 -0
  134. package/src/web/convert/object/index.ts +2 -0
  135. package/src/web/convert/utils.ts +11 -0
  136. package/src/web/registry.ts +7 -8
  137. package/src/web/runtime.ts +7 -7
  138. package/src/web/utils/unistyle.ts +2 -4
  139. package/lib/commonjs/web/convert/module.d.js +0 -2
  140. package/lib/commonjs/web/convert/module.d.js.map +0 -1
  141. package/lib/module/web/convert/module.d.js +0 -2
  142. package/lib/module/web/convert/module.d.js.map +0 -1
@@ -1,10 +1,10 @@
1
1
  import React, {
2
- forwardRef,
3
2
  useContext,
4
3
  useEffect,
5
4
  type ComponentProps,
6
5
  type ComponentRef,
7
6
  type ComponentType,
7
+ type Ref,
8
8
  } from 'react'
9
9
 
10
10
  import type { UnistylesValues } from '../../types'
@@ -12,7 +12,7 @@ import type { Mappings } from './types'
12
12
 
13
13
  import { ScopedThemeContext } from '../../components/ScopedThemeContext'
14
14
  import { type UnistyleDependency, UnistylesShadowRegistry } from '../../specs'
15
- import { deepMergeObjects } from '../../utils'
15
+ import { deepMergeObjects, isAnimatedStyleHandle } from '../../utils'
16
16
  import { useProxifiedUnistyles } from '../useProxifiedUnistyles'
17
17
  import { maybeWarnAboutMultipleUnistyles } from '../warn'
18
18
 
@@ -27,10 +27,30 @@ type UnistylesSecrets = {
27
27
  }
28
28
 
29
29
  type MappedSecrets = {
30
- styles: Record<string, any>
30
+ styles: Record<string, any> | Array<Record<string, any>>
31
31
  dependencies: Array<UnistyleDependency>
32
32
  }
33
33
 
34
+ // Reanimated finds animated styles by their keys, merged with other styles they would hide them
35
+ // so keep every animated style as a separate entry and merge only the styles between them, in order
36
+ const mergeAroundAnimatedStyles = (secrets: Array<UnistylesSecrets>) =>
37
+ secrets.reduce<Array<Record<string, any>>>((acc, secret) => {
38
+ const style = secret.uni__getStyles()
39
+ const lastStyle = acc.at(-1)
40
+
41
+ if (isAnimatedStyleHandle(style)) {
42
+ return acc.concat([style])
43
+ }
44
+
45
+ if (lastStyle && !isAnimatedStyleHandle(lastStyle)) {
46
+ Object.assign(lastStyle, style)
47
+
48
+ return acc
49
+ }
50
+
51
+ return acc.concat([{ ...style }])
52
+ }, [])
53
+
34
54
  export const getSecrets = (styleProps: Record<string, any> = {}): MappedSecrets => {
35
55
  const styles = Array.isArray(styleProps) ? styleProps.flat(Infinity) : [styleProps]
36
56
 
@@ -48,7 +68,9 @@ export const getSecrets = (styleProps: Record<string, any> = {}): MappedSecrets
48
68
  }, [])
49
69
 
50
70
  return {
51
- styles: secrets.reduce((acc, secret) => Object.assign(acc, secret.uni__getStyles()), {} as Record<string, any>),
71
+ styles: styles.some(isAnimatedStyleHandle)
72
+ ? mergeAroundAnimatedStyles(secrets)
73
+ : secrets.reduce((acc, secret) => Object.assign(acc, secret.uni__getStyles()), {} as Record<string, any>),
52
74
  dependencies: secrets.flatMap((secret) => secret.uni__dependencies),
53
75
  }
54
76
  }
@@ -66,7 +88,7 @@ export const withUnistyles = <TComponent, TMappings extends GenericComponentProp
66
88
  contentContainerStyle?: UnistylesValues
67
89
  }
68
90
 
69
- return forwardRef<GenericComponentRef<TComponent>, PropsWithUnistyles>((props, ref) => {
91
+ return ({ ref, ...props }: PropsWithUnistyles & { ref?: Ref<GenericComponentRef<TComponent>> }) => {
70
92
  const narrowedProps = props as PropsWithUnistyles & UnistyleStyles
71
93
  const NativeComponent = Component as ComponentType
72
94
 
@@ -127,5 +149,5 @@ export const withUnistyles = <TComponent, TMappings extends GenericComponentProp
127
149
  } as any
128
150
 
129
151
  return <NativeComponent key={uniPropsKey || mappingsKey} {...finalProps} ref={ref} />
130
- })
152
+ }
131
153
  }
@@ -1,4 +1,4 @@
1
- import React, { type ComponentType, forwardRef, type ComponentProps, type ComponentRef } from 'react'
1
+ import React, { type ComponentType, type ComponentProps, type ComponentRef, type Ref } from 'react'
2
2
 
3
3
  import type { UnistylesValues } from '../../types'
4
4
  import type { Mappings } from './types'
@@ -26,9 +26,9 @@ export const withUnistyles = <TComponent, TMappings extends GenericComponentProp
26
26
  contentContainerStyle?: UnistylesValues
27
27
  }
28
28
 
29
- return forwardRef<GenericComponentRef<TComponent>, PropsWithUnistyles>((props, ref) => {
29
+ return ({ ref, ...props }: PropsWithUnistyles & { ref?: Ref<GenericComponentRef<TComponent>> }) => {
30
30
  const narrowedProps = props as PropsWithUnistyles & UnistyleStyles
31
- const styleClassNames = getClassName(narrowedProps.style, true)
31
+ const [classNames, animatedStyles] = getClassName(narrowedProps.style, true) ?? []
32
32
  const contentContainerStyleClassNames = getClassName(narrowedProps.contentContainerStyle)
33
33
  const { proxifiedRuntime, proxifiedTheme } = useProxifiedUnistyles()
34
34
 
@@ -50,7 +50,8 @@ export const withUnistyles = <TComponent, TMappings extends GenericComponentProp
50
50
  ...(narrowedProps.style
51
51
  ? {
52
52
  // Override default component styles with undefined values to reset them
53
- style: emptyStyles,
53
+ // animated styles can't be a class, so they stay inline
54
+ style: animatedStyles?.length ? [emptyStyles, ...animatedStyles] : emptyStyles,
54
55
  }
55
56
  : {}),
56
57
  ...(narrowedProps.contentContainerStyle
@@ -68,12 +69,11 @@ export const withUnistyles = <TComponent, TMappings extends GenericComponentProp
68
69
  maybeWarnAboutMultipleUnistyles(narrowedProps.contentContainerStyle, `withUnistyles(${Component.displayName ?? Component.name ?? 'Unknown'})`)
69
70
 
70
71
  const NativeComponent = Component as ComponentType
71
- const [classNames] = styleClassNames ?? []
72
72
 
73
73
  return (
74
74
  <div className={classNames?.hash} style={{ display: 'contents' }}>
75
75
  <NativeComponent key={uniPropsKey || mappingsKey} {...combinedProps} ref={ref} />
76
76
  </div>
77
77
  )
78
- })
78
+ }
79
79
  }
package/src/mocks.ts CHANGED
@@ -20,6 +20,7 @@ jest.mock('react-native-nitro-modules', () => ({
20
20
  createHybridObject: () => ({
21
21
  add: () => {},
22
22
  init: () => {},
23
+ addChangeListener: () => () => {},
23
24
  createHybridStatusBar: () => ({
24
25
  setStyle: () => {},
25
26
  }),
@@ -199,11 +200,19 @@ jest.mock('react-native-unistyles', () => {
199
200
  Component: TComponent,
200
201
  mapper?: (theme: UnistylesTheme, runtime: typeof miniRuntime) => TComponent,
201
202
  ) =>
202
- (props: any) =>
203
- React.createElement(Component, {
204
- ...mapper?.((Object.values(_REGISTRY.themes).at(0) ?? {}) as UnistylesTheme, miniRuntime),
205
- ...props,
206
- }),
203
+ ({ uniProps, ...props }: any) => {
204
+ const { deepMergeObjects } = require('./utils')
205
+ const theme = (Object.values(_REGISTRY.themes).at(0) ?? {}) as UnistylesTheme
206
+ const { key: mappingsKey, ...mappingsProps } = mapper?.(theme, miniRuntime) ?? ({} as any)
207
+ const { key: uniPropsKey, ...unistyleProps } = uniProps?.(theme, miniRuntime) ?? {}
208
+
209
+ return React.createElement(Component, {
210
+ ...deepMergeObjects(mappingsProps, unistyleProps, props),
211
+ ...(props.style ? { style: props.style } : {}),
212
+ ...(props.contentContainerStyle ? { contentContainerStyle: props.contentContainerStyle } : {}),
213
+ key: uniPropsKey || mappingsKey,
214
+ })
215
+ },
207
216
  mq: {
208
217
  only: {
209
218
  width: () => ({
@@ -9,6 +9,7 @@ import type { ShadowNode, Unistyle, ViewHandle } from './types'
9
9
  import { Fiber, Reanimated } from '../../unifiber/src'
10
10
  import { StyleSheet } from '../StyleSheet'
11
11
  import { createUnistylesStyleSource } from './styleSource'
12
+ import { traceSection } from './tracing'
12
13
 
13
14
  interface ShadowRegistry extends UnistylesShadowRegistrySpec {
14
15
  // Babel API
@@ -74,7 +75,7 @@ const refreshReactNodes = () => {
74
75
  HybridShadowRegistry.refreshReactNodes(refreshNodes)
75
76
  }
76
77
 
77
- owners.refresh(committedHosts)
78
+ traceSection('refreshOwners', () => owners.refresh(committedHosts))
78
79
  } finally {
79
80
  committedHosts.length = 0
80
81
  refreshNodes = []
@@ -83,6 +84,16 @@ const refreshReactNodes = () => {
83
84
 
84
85
  StyleSheet.addChangeListener(refreshReactNodes)
85
86
 
87
+ // a unistyle key holds its secrets object. A unistyle spread into another StyleSheet.create keeps its key,
88
+ // but the parser resolves the (hidden) secrets to undefined, so the key no longer points at a unistyle
89
+ const isUnistyleKey = (style: any, key: string) => {
90
+ if (!key.startsWith('unistyles_')) {
91
+ return false
92
+ }
93
+
94
+ return typeof style[key] === 'object' && style[key] !== null
95
+ }
96
+
86
97
  // skips empty styles and splits a style merged from many unistyles (eg. flattened by Animated) into one per unistyle
87
98
  // runs for every style on every link, so it reads the keys once and allocates only for the rare merged style
88
99
  const collectStyle = (style: any, collected: Array<any>) => {
@@ -94,7 +105,7 @@ const collectStyle = (style: any, collected: Array<any>) => {
94
105
  let unistylesCount = 0
95
106
 
96
107
  for (const key of keys) {
97
- if (key.startsWith('unistyles_')) {
108
+ if (isUnistyleKey(style, key)) {
98
109
  unistylesCount++
99
110
  }
100
111
  }
@@ -108,7 +119,7 @@ const collectStyle = (style: any, collected: Array<any>) => {
108
119
  }
109
120
 
110
121
  for (const key of keys) {
111
- if (key.startsWith('unistyles_')) {
122
+ if (isUnistyleKey(style, key)) {
112
123
  collected.push({ [key]: style[key] })
113
124
  }
114
125
  }
@@ -0,0 +1,23 @@
1
+ // console.timeStamp(label, start, end, trackName), React Native 0.86+ reports it only while DevTools records and keeps
2
+ // nothing, unlike performance.measure, which adds every entry to an unbounded buffer
3
+ type TracingConsole = {
4
+ timeStamp?: (label: string, start: number, end: number, trackName: string) => void
5
+ }
6
+
7
+ const tracingConsole = console as unknown as TracingConsole
8
+ const canTrace = __DEV__ && typeof tracingConsole.timeStamp === 'function'
9
+
10
+ // Slice on the "Unistyles" track of the React Native DevTools Performance panel, next to the native ones
11
+ export const traceSection = <T>(name: string, fn: () => T): T => {
12
+ if (!canTrace) {
13
+ return fn()
14
+ }
15
+
16
+ const start = performance.now()
17
+
18
+ try {
19
+ return fn()
20
+ } finally {
21
+ tracingConsole.timeStamp?.(name, start, performance.now(), 'Unistyles')
22
+ }
23
+ }
package/src/utils.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  export const isDefined = <T>(value: T): value is NonNullable<T> => value !== undefined && value !== null
2
2
 
3
+ // Reanimated useAnimatedStyle handle, it's a plain object, not an Animated.Node
4
+ // styleUpdaterContainer was added in Reanimated 4.1, so check only the keys every version has
5
+ export const isAnimatedStyleHandle = (value: unknown): boolean =>
6
+ typeof value === 'object' && value !== null && 'viewDescriptors' in value && 'initial' in value
7
+
3
8
  export const deepMergeObjects = <T extends Record<PropertyKey, any>>(...sources: Array<T>) => {
4
9
  const target = {} as T
5
10
 
@@ -23,7 +28,53 @@ export const deepMergeObjects = <T extends Record<PropertyKey, any>>(...sources:
23
28
  return target
24
29
  }
25
30
 
31
+ // React tells a class component from a function component by these keys
32
+ const CLASS_PROTOTYPE_KEYS = ['isReactComponent', 'render']
33
+ const CLASS_STATIC_KEYS = ['contextType', 'contextTypes', 'childContextTypes', 'getDerivedStateFromProps']
34
+
35
+ // owner doesn't have the key, objects that inherit from owner read it from source
36
+ const hideFromOwner = (owner: object, source: object, key: string) => {
37
+ Object.defineProperty(owner, key, {
38
+ configurable: true,
39
+ get() {
40
+ return this === owner ? undefined : Reflect.get(source, key, this)
41
+ },
42
+ set(value) {
43
+ Object.defineProperty(this, key, { value, writable: true, enumerable: true, configurable: true })
44
+ },
45
+ })
46
+ }
47
+
48
+ const inheritNativeClass = (Component: any, UnistylesComponent: (props: unknown) => unknown) => {
49
+ function UnistylesClassComponent(this: unknown, ...args: Array<unknown>) {
50
+ if (this instanceof UnistylesClassComponent) {
51
+ return Reflect.construct(Component, args, new.target ?? this.constructor)
52
+ }
53
+
54
+ return UnistylesComponent(args[0])
55
+ }
56
+
57
+ Object.setPrototypeOf(UnistylesClassComponent, Component)
58
+ UnistylesClassComponent.prototype = Object.create(Component.prototype, {
59
+ constructor: { value: UnistylesClassComponent, writable: true, configurable: true },
60
+ })
61
+ // only the keys of the class, eg. React warns about a subclass with a `contextType` key that isn't a context
62
+ CLASS_PROTOTYPE_KEYS.filter((key) => key in Component.prototype).forEach((key) =>
63
+ hideFromOwner(UnistylesClassComponent.prototype, Component.prototype, key),
64
+ )
65
+ CLASS_STATIC_KEYS.filter((key) => key in Component).forEach((key) =>
66
+ hideFromOwner(UnistylesClassComponent, Component, key),
67
+ )
68
+
69
+ return UnistylesClassComponent
70
+ }
71
+
26
72
  export const copyComponentProperties = (Component: any, UnistylesComponent: any) => {
73
+ // static properties (eg. displayName) are inherited from the class
74
+ if (Component?.prototype?.isReactComponent && typeof UnistylesComponent === 'function') {
75
+ return inheritNativeClass(Component, UnistylesComponent)
76
+ }
77
+
27
78
  Object.entries(Component).forEach(([key, value]) => {
28
79
  // Filter out the keys we don't want to copy
29
80
  if (['$$typeof', 'render', 'contextType'].includes(key)) {
@@ -2,11 +2,38 @@ import type { UnistylesValues } from '../../types'
2
2
  import type { UnistylesRuntime } from '../runtime'
3
3
 
4
4
  import { deepMergeObjects } from '../../utils'
5
- import { getBoxShadow, getFilterStyle, getTransformStyle } from './object'
5
+ import {
6
+ getBackgroundLayers,
7
+ getBoxShadow,
8
+ getFilterStyle,
9
+ getFontVariationSettings,
10
+ getTransformStyle,
11
+ } from './object'
6
12
  import { isPseudo } from './pseudo'
7
13
  import { getBoxShadowStyle, getTextShadowStyle } from './shadow'
8
14
  import { getStyle } from './style'
9
- import { isBoxShadow, isFilter, isShadow, isTextShadow, isTransform } from './utils'
15
+ import {
16
+ isBackgroundLayer,
17
+ isBoxShadow,
18
+ isFilter,
19
+ isFontVariationSettings,
20
+ isShadow,
21
+ isTextShadow,
22
+ isTransform,
23
+ } from './utils'
24
+
25
+ // a breakpoint can hold React Native's array or object syntax too
26
+ const getBreakpointStyle = (key: string, value: any) => {
27
+ if (isBackgroundLayer(key, value)) {
28
+ return getBackgroundLayers(key, value)
29
+ }
30
+
31
+ if (isFontVariationSettings(key, value)) {
32
+ return getFontVariationSettings(value)
33
+ }
34
+
35
+ return getStyle(key, value)
36
+ }
10
37
 
11
38
  export const convertUnistyles = (value: UnistylesValues, runtime: UnistylesRuntime) => {
12
39
  // Flag to mark if textShadow is already created
@@ -65,10 +92,19 @@ export const convertUnistyles = (value: UnistylesValues, runtime: UnistylesRunti
65
92
  return getTransformStyle(unistylesValue)
66
93
  }
67
94
 
95
+ // React Native's array and object syntaxes, not breakpoints
96
+ if (isBackgroundLayer(unistylesKey, unistylesValue)) {
97
+ return getBackgroundLayers(unistylesKey, unistylesValue)
98
+ }
99
+
100
+ if (isFontVariationSettings(unistylesKey, unistylesValue)) {
101
+ return getFontVariationSettings(unistylesValue)
102
+ }
103
+
68
104
  // Breakpoints
69
105
  if (typeof unistylesValue === 'object' && unistylesValue !== null) {
70
106
  return Object.entries(unistylesValue).map(([breakpointKey, breakpointValue]) => {
71
- return { [breakpointKey]: getStyle(unistylesKey, breakpointValue) }
107
+ return { [breakpointKey]: getBreakpointStyle(unistylesKey, breakpointValue) }
72
108
  })
73
109
  }
74
110
 
@@ -0,0 +1,33 @@
1
+ import { normalizeNumericValue } from '../utils'
2
+
3
+ type BackgroundLayerKey = 'backgroundSize' | 'backgroundPosition' | 'backgroundRepeat'
4
+ type BackgroundSize = string | { x: string | number; y: string | number }
5
+ type BackgroundPosition = Partial<Record<'top' | 'left' | 'right' | 'bottom', string | number>>
6
+ type BackgroundRepeat = { x: string; y: string }
7
+
8
+ const createBackgroundSizeValue = (size: BackgroundSize) => {
9
+ if (typeof size === 'string') {
10
+ return size
11
+ }
12
+
13
+ return `${normalizeNumericValue(size.x)} ${normalizeNumericValue(size.y)}`
14
+ }
15
+
16
+ const createBackgroundPositionValue = (position: BackgroundPosition) => {
17
+ const edges = (['left', 'right', 'top', 'bottom'] as const).filter((edge) => position[edge] !== undefined)
18
+
19
+ return edges.map((edge) => `${edge} ${normalizeNumericValue(position[edge]!)}`).join(' ')
20
+ }
21
+
22
+ const createBackgroundRepeatValue = (repeat: BackgroundRepeat) => `${repeat.x} ${repeat.y}`
23
+
24
+ const createBackgroundLayerValue = {
25
+ backgroundSize: createBackgroundSizeValue,
26
+ backgroundPosition: createBackgroundPositionValue,
27
+ backgroundRepeat: createBackgroundRepeatValue,
28
+ } as Record<BackgroundLayerKey, (layer: any) => string>
29
+
30
+ // React Native's processed syntax, one object per background layer
31
+ export const getBackgroundLayers = (key: string, layers: Array<unknown>) => ({
32
+ [key]: layers.map(createBackgroundLayerValue[key as BackgroundLayerKey]).join(', '),
33
+ })
@@ -0,0 +1,7 @@
1
+ // Same output as React Native's processFontVariationSettings, eg. { wght: 700 } -> 'wght' 700
2
+ export const getFontVariationSettings = (fontVariationSettings: Record<string, number>) => ({
3
+ fontVariationSettings: Object.keys(fontVariationSettings)
4
+ .sort()
5
+ .map((axis) => `${axis.includes("'") ? `"${axis}"` : `'${axis}'`} ${fontVariationSettings[axis]}`)
6
+ .join(', '),
7
+ })
@@ -1,3 +1,5 @@
1
1
  export * from './transform'
2
2
  export * from './boxShadow'
3
3
  export * from './filter'
4
+ export * from './backgroundLayers'
5
+ export * from './fontVariationSettings'
@@ -21,6 +21,17 @@ export const isFilter = (key: string, value: any): value is Array<Filters> => ke
21
21
  export const isBoxShadow = (key: string, value: any): value is Array<BoxShadowValue> =>
22
22
  key === 'boxShadow' && Array.isArray(value)
23
23
 
24
+ export const isBackgroundLayer = (key: string, value: any): value is Array<unknown> =>
25
+ ['backgroundSize', 'backgroundPosition', 'backgroundRepeat'].includes(key) && Array.isArray(value)
26
+
27
+ // { wght: 700 } is the object syntax, a breakpoints object holds strings or objects
28
+ export const isFontVariationSettings = (key: string, value: any): value is Record<string, number> =>
29
+ key === 'fontVariationSettings' &&
30
+ typeof value === 'object' &&
31
+ value !== null &&
32
+ !Array.isArray(value) &&
33
+ Object.values(value).every((axisValue) => typeof axisValue === 'number')
34
+
24
35
  export const normalizeNumericValue = (value: number | string) =>
25
36
  value && typeof value === 'number' ? `${value}px` : value
26
37
 
@@ -32,7 +32,7 @@ export class UnistylesRegistry {
32
32
  )
33
33
  }
34
34
 
35
- return stylesheet(scopedTheme, this.services.runtime.miniRuntime)
35
+ return stylesheet(scopedTheme, this.services.runtime.getMiniRuntime(scopedThemeName))
36
36
  }
37
37
 
38
38
  const computedStylesheet = this.stylesheets.get(stylesheet)
@@ -41,11 +41,9 @@ export class UnistylesRegistry {
41
41
  return computedStylesheet
42
42
  }
43
43
 
44
- const currentTheme = this.services.runtime.getTheme(
45
- this.services.runtime.themeName,
46
- this.services.state.CSSVars,
47
- )
48
- const createdStylesheet = stylesheet(currentTheme, this.services.runtime.miniRuntime)
44
+ const themeName = this.services.runtime.themeName
45
+ const currentTheme = this.services.runtime.getTheme(themeName, this.services.state.CSSVars)
46
+ const createdStylesheet = stylesheet(currentTheme, this.services.runtime.getMiniRuntime(themeName))
49
47
  const dependencies = Object.values(createdStylesheet).flatMap((value) => extractUnistyleDependencies(value))
50
48
 
51
49
  this.addDependenciesToStylesheet(stylesheet, dependencies)
@@ -65,9 +63,10 @@ export class UnistylesRegistry {
65
63
  dependencies.forEach((dependency) => dependenciesMap.add(dependency))
66
64
 
67
65
  const dispose = this.services.listener.addStylesheetListeners(Array.from(dependenciesMap), () => {
66
+ const themeName = this.services.runtime.themeName
68
67
  const newComputedStylesheet = stylesheet(
69
- this.services.runtime.getTheme(this.services.runtime.themeName, this.services.state.CSSVars),
70
- this.services.runtime.miniRuntime,
68
+ this.services.runtime.getTheme(themeName, this.services.state.CSSVars),
69
+ this.services.runtime.getMiniRuntime(themeName),
71
70
  )
72
71
 
73
72
  this.stylesheets.set(stylesheet, newComputedStylesheet)
@@ -49,13 +49,8 @@ export class UnistylesRuntime {
49
49
  }
50
50
  }
51
51
 
52
+ // App theme, like on native. ScopedTheme is read from React Context (useUnistyles) (#1295)
52
53
  get themeName() {
53
- const scopedTheme = this.services.shadowRegistry.getScopedTheme()
54
-
55
- if (scopedTheme) {
56
- return scopedTheme
57
- }
58
-
59
54
  if (this.services.state.hasAdaptiveThemes) {
60
55
  return schemeToTheme(this.colorScheme) as AppThemeName
61
56
  }
@@ -150,9 +145,14 @@ export class UnistylesRuntime {
150
145
  }
151
146
 
152
147
  get miniRuntime(): UnistylesMiniRuntime {
148
+ return this.getMiniRuntime(this.themeName)
149
+ }
150
+
151
+ // StyleSheets get the name of the theme they are called with
152
+ getMiniRuntime = (themeName?: AppThemeName): UnistylesMiniRuntime => {
153
153
  return {
154
154
  colorScheme: this.colorScheme,
155
- themeName: this.themeName,
155
+ themeName,
156
156
  contentSizeCategory: this.contentSizeCategory,
157
157
  breakpoint: this.breakpoint,
158
158
  isLandscape: this.orientation === Orientation.Landscape,
@@ -6,7 +6,7 @@ import type { StyleSheet, StyleSheetWithSuperPowers, UnistylesValues } from '../
6
6
  import type { UnistylesRuntime } from '../runtime'
7
7
 
8
8
  import { ColorScheme, Orientation } from '../../specs/types'
9
- import { isDefined, isUnistylesMq, parseMq } from '../../utils'
9
+ import { isAnimatedStyleHandle, isDefined, isUnistylesMq, parseMq } from '../../utils'
10
10
  import * as unistyles from '../services'
11
11
  import { UNI_GENERATED_KEYS, type UniGeneratedKey, type UniGeneratedStyle } from '../types'
12
12
  import { hyphenate, keyInObject, reduceObject } from './common'
@@ -142,9 +142,7 @@ export const checkForAnimated = (value: any): boolean => {
142
142
  }
143
143
 
144
144
  if (typeof value === 'object' && value !== null) {
145
- // Reanimated useAnimatedStyle handle, it's a plain object, not an Animated.Node
146
- // styleUpdaterContainer was added in Reanimated 4.1, so check only the keys every version has
147
- if ('viewDescriptors' in value && 'initial' in value) {
145
+ if (isAnimatedStyleHandle(value)) {
148
146
  return true
149
147
  }
150
148
 
@@ -1,2 +0,0 @@
1
- "use strict";
2
- //# sourceMappingURL=module.d.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":[],"sourceRoot":"../../../../src","sources":["web/convert/module.d.ts"],"mappings":"","ignoreList":[]}
@@ -1,2 +0,0 @@
1
- "use strict";
2
- //# sourceMappingURL=module.d.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":[],"sourceRoot":"../../../../src","sources":["web/convert/module.d.ts"],"mappings":"","ignoreList":[]}