react-native-unistyles 3.5.1 → 3.5.3

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 (143) hide show
  1. package/Unistyles.podspec +1 -0
  2. package/android/src/main/java/com/unistyles/NativePlatform+android.kt +44 -7
  3. package/android/src/main/java/com/unistyles/NativePlatform+insets.kt +1 -1
  4. package/cxx/core/UnistylesRegistry.cpp +5 -5
  5. package/cxx/core/UnistylesRegistry.h +3 -3
  6. package/cxx/hybridObjects/HybridShadowRegistry.cpp +48 -14
  7. package/cxx/hybridObjects/HybridShadowRegistry.h +3 -2
  8. package/cxx/hybridObjects/HybridStyleSheet.cpp +3 -3
  9. package/cxx/parser/Parser.cpp +18 -2
  10. package/cxx/shadowTree/ShadowTreeManager.cpp +5 -5
  11. package/cxx/shadowTree/ShadowTreeManager.h +1 -1
  12. package/ios/NativePlatform+ios.swift +6 -4
  13. package/lib/commonjs/components/AdaptiveTheme.js +9 -1
  14. package/lib/commonjs/components/AdaptiveTheme.js.map +1 -1
  15. package/lib/commonjs/components/NamedTheme.js +9 -1
  16. package/lib/commonjs/components/NamedTheme.js.map +1 -1
  17. package/lib/commonjs/components/ScopedThemeContext.js +9 -0
  18. package/lib/commonjs/components/ScopedThemeContext.js.map +1 -0
  19. package/lib/commonjs/components/native/Pressable.native.js +6 -5
  20. package/lib/commonjs/components/native/Pressable.native.js.map +1 -1
  21. package/lib/commonjs/components/scopeChildren.js +29 -0
  22. package/lib/commonjs/components/scopeChildren.js.map +1 -0
  23. package/lib/commonjs/components/scopeChildren.native.js +9 -0
  24. package/lib/commonjs/components/scopeChildren.native.js.map +1 -0
  25. package/lib/commonjs/core/createUnistylesActivityIndicator.js +5 -3
  26. package/lib/commonjs/core/createUnistylesActivityIndicator.js.map +1 -1
  27. package/lib/commonjs/core/createUnistylesElement.native.js +3 -1
  28. package/lib/commonjs/core/createUnistylesElement.native.js.map +1 -1
  29. package/lib/commonjs/core/createUnistylesImageBackground.js +7 -4
  30. package/lib/commonjs/core/createUnistylesImageBackground.js.map +1 -1
  31. package/lib/commonjs/core/createUnistylesTouchableHighlight.js +6 -8
  32. package/lib/commonjs/core/createUnistylesTouchableHighlight.js.map +1 -1
  33. package/lib/commonjs/core/useProxifiedUnistyles/useProxifiedUnistyles.js +20 -5
  34. package/lib/commonjs/core/useProxifiedUnistyles/useProxifiedUnistyles.js.map +1 -1
  35. package/lib/commonjs/core/withUnistyles/withUnistyles.native.js +28 -17
  36. package/lib/commonjs/core/withUnistyles/withUnistyles.native.js.map +1 -1
  37. package/lib/commonjs/reanimated/useAnimatedTheme.js +8 -5
  38. package/lib/commonjs/reanimated/useAnimatedTheme.js.map +1 -1
  39. package/lib/commonjs/reanimated/useAnimatedTheme.native.js +8 -5
  40. package/lib/commonjs/reanimated/useAnimatedTheme.native.js.map +1 -1
  41. package/lib/commonjs/specs/ShadowRegistry/index.js +76 -74
  42. package/lib/commonjs/specs/ShadowRegistry/index.js.map +1 -1
  43. package/lib/commonjs/web/css/core.js +30 -5
  44. package/lib/commonjs/web/css/core.js.map +1 -1
  45. package/lib/commonjs/web/css/state.js +9 -3
  46. package/lib/commonjs/web/css/state.js.map +1 -1
  47. package/lib/commonjs/web/registry.js +1 -1
  48. package/lib/commonjs/web/registry.js.map +1 -1
  49. package/lib/commonjs/web/shadowRegistry.js +3 -1
  50. package/lib/commonjs/web/shadowRegistry.js.map +1 -1
  51. package/lib/module/components/AdaptiveTheme.js +10 -2
  52. package/lib/module/components/AdaptiveTheme.js.map +1 -1
  53. package/lib/module/components/NamedTheme.js +10 -2
  54. package/lib/module/components/NamedTheme.js.map +1 -1
  55. package/lib/module/components/ScopedThemeContext.js +5 -0
  56. package/lib/module/components/ScopedThemeContext.js.map +1 -0
  57. package/lib/module/components/native/Pressable.native.js +7 -6
  58. package/lib/module/components/native/Pressable.native.js.map +1 -1
  59. package/lib/module/components/scopeChildren.js +23 -0
  60. package/lib/module/components/scopeChildren.js.map +1 -0
  61. package/lib/module/components/scopeChildren.native.js +4 -0
  62. package/lib/module/components/scopeChildren.native.js.map +1 -0
  63. package/lib/module/core/createUnistylesActivityIndicator.js +4 -2
  64. package/lib/module/core/createUnistylesActivityIndicator.js.map +1 -1
  65. package/lib/module/core/createUnistylesElement.native.js +4 -2
  66. package/lib/module/core/createUnistylesElement.native.js.map +1 -1
  67. package/lib/module/core/createUnistylesImageBackground.js +6 -3
  68. package/lib/module/core/createUnistylesImageBackground.js.map +1 -1
  69. package/lib/module/core/createUnistylesTouchableHighlight.js +7 -9
  70. package/lib/module/core/createUnistylesTouchableHighlight.js.map +1 -1
  71. package/lib/module/core/useProxifiedUnistyles/useProxifiedUnistyles.js +22 -7
  72. package/lib/module/core/useProxifiedUnistyles/useProxifiedUnistyles.js.map +1 -1
  73. package/lib/module/core/withUnistyles/withUnistyles.native.js +27 -17
  74. package/lib/module/core/withUnistyles/withUnistyles.native.js.map +1 -1
  75. package/lib/module/reanimated/useAnimatedTheme.js +10 -7
  76. package/lib/module/reanimated/useAnimatedTheme.js.map +1 -1
  77. package/lib/module/reanimated/useAnimatedTheme.native.js +10 -7
  78. package/lib/module/reanimated/useAnimatedTheme.native.js.map +1 -1
  79. package/lib/module/specs/ShadowRegistry/index.js +76 -74
  80. package/lib/module/specs/ShadowRegistry/index.js.map +1 -1
  81. package/lib/module/web/css/core.js +28 -4
  82. package/lib/module/web/css/core.js.map +1 -1
  83. package/lib/module/web/css/state.js +10 -4
  84. package/lib/module/web/css/state.js.map +1 -1
  85. package/lib/module/web/registry.js +1 -1
  86. package/lib/module/web/registry.js.map +1 -1
  87. package/lib/module/web/shadowRegistry.js +3 -1
  88. package/lib/module/web/shadowRegistry.js.map +1 -1
  89. package/lib/typescript/src/__tests__/scoped-theme.spec.d.ts +2 -0
  90. package/lib/typescript/src/__tests__/scoped-theme.spec.d.ts.map +1 -0
  91. package/lib/typescript/src/__tests__/shadow-registry.spec.d.ts +2 -0
  92. package/lib/typescript/src/__tests__/shadow-registry.spec.d.ts.map +1 -0
  93. package/lib/typescript/src/__tests__/web-child-selector.spec.d.ts +2 -0
  94. package/lib/typescript/src/__tests__/web-child-selector.spec.d.ts.map +1 -0
  95. package/lib/typescript/src/__tests__/web-pointer-events.spec.d.ts +2 -0
  96. package/lib/typescript/src/__tests__/web-pointer-events.spec.d.ts.map +1 -0
  97. package/lib/typescript/src/__tests__/withUnistyles.spec.d.ts +2 -0
  98. package/lib/typescript/src/__tests__/withUnistyles.spec.d.ts.map +1 -0
  99. package/lib/typescript/src/components/AdaptiveTheme.d.ts.map +1 -1
  100. package/lib/typescript/src/components/NamedTheme.d.ts.map +1 -1
  101. package/lib/typescript/src/components/ScopedThemeContext.d.ts +6 -0
  102. package/lib/typescript/src/components/ScopedThemeContext.d.ts.map +1 -0
  103. package/lib/typescript/src/components/native/Pressable.native.d.ts.map +1 -1
  104. package/lib/typescript/src/components/scopeChildren.d.ts +4 -0
  105. package/lib/typescript/src/components/scopeChildren.d.ts.map +1 -0
  106. package/lib/typescript/src/components/scopeChildren.native.d.ts +4 -0
  107. package/lib/typescript/src/components/scopeChildren.native.d.ts.map +1 -0
  108. package/lib/typescript/src/core/createUnistylesActivityIndicator.d.ts.map +1 -1
  109. package/lib/typescript/src/core/createUnistylesElement.native.d.ts.map +1 -1
  110. package/lib/typescript/src/core/createUnistylesImageBackground.d.ts.map +1 -1
  111. package/lib/typescript/src/core/createUnistylesTouchableHighlight.d.ts.map +1 -1
  112. package/lib/typescript/src/core/useProxifiedUnistyles/useProxifiedUnistyles.d.ts +1 -2
  113. package/lib/typescript/src/core/useProxifiedUnistyles/useProxifiedUnistyles.d.ts.map +1 -1
  114. package/lib/typescript/src/core/withUnistyles/withUnistyles.native.d.ts +6 -0
  115. package/lib/typescript/src/core/withUnistyles/withUnistyles.native.d.ts.map +1 -1
  116. package/lib/typescript/src/reanimated/useAnimatedTheme.d.ts.map +1 -1
  117. package/lib/typescript/src/reanimated/useAnimatedTheme.native.d.ts.map +1 -1
  118. package/lib/typescript/src/specs/ShadowRegistry/index.d.ts +4 -3
  119. package/lib/typescript/src/specs/ShadowRegistry/index.d.ts.map +1 -1
  120. package/lib/typescript/src/web/css/core.d.ts +1 -0
  121. package/lib/typescript/src/web/css/core.d.ts.map +1 -1
  122. package/lib/typescript/src/web/css/state.d.ts.map +1 -1
  123. package/lib/typescript/src/web/shadowRegistry.d.ts.map +1 -1
  124. package/package.json +1 -1
  125. package/src/components/AdaptiveTheme.tsx +8 -2
  126. package/src/components/NamedTheme.tsx +8 -2
  127. package/src/components/ScopedThemeContext.ts +9 -0
  128. package/src/components/native/Pressable.native.tsx +8 -7
  129. package/src/components/scopeChildren.native.ts +5 -0
  130. package/src/components/scopeChildren.tsx +19 -0
  131. package/src/core/createUnistylesActivityIndicator.tsx +5 -2
  132. package/src/core/createUnistylesElement.native.tsx +4 -2
  133. package/src/core/createUnistylesImageBackground.tsx +6 -3
  134. package/src/core/createUnistylesTouchableHighlight.tsx +7 -11
  135. package/src/core/useProxifiedUnistyles/useProxifiedUnistyles.ts +22 -10
  136. package/src/core/withUnistyles/withUnistyles.native.tsx +46 -30
  137. package/src/reanimated/useAnimatedTheme.native.ts +9 -9
  138. package/src/reanimated/useAnimatedTheme.ts +9 -9
  139. package/src/specs/ShadowRegistry/index.ts +93 -91
  140. package/src/web/css/core.ts +29 -4
  141. package/src/web/css/state.ts +16 -4
  142. package/src/web/registry.ts +1 -1
  143. package/src/web/shadowRegistry.ts +3 -1
@@ -1,20 +1,20 @@
1
- import { useEffect, useState } from 'react'
1
+ import { useContext, useEffect, useState } from 'react'
2
2
  import { type SharedValue, useSharedValue } from 'react-native-reanimated'
3
3
 
4
4
  import type { UnistylesTheme } from '../types'
5
5
 
6
- import { StyleSheet, UnistyleDependency, UnistylesRuntime, UnistylesShadowRegistry } from '../specs'
6
+ import { ScopedThemeContext } from '../components/ScopedThemeContext'
7
+ import { StyleSheet, UnistyleDependency, UnistylesRuntime } from '../specs'
7
8
 
8
9
  export const useAnimatedTheme = () => {
9
- const [scopedTheme, setScopedTheme] = useState(
10
- () => UnistylesShadowRegistry.getScopedTheme() as UnistylesTheme | undefined,
11
- )
10
+ const scope = useContext(ScopedThemeContext)
11
+ const [scopedTheme, setScopedTheme] = useState(scope?.name as UnistylesTheme | undefined)
12
12
  const theme = useSharedValue(UnistylesRuntime.getTheme(scopedTheme))
13
- const maybeNewScopedTheme = UnistylesShadowRegistry.getScopedTheme() as UnistylesTheme | undefined
14
13
 
15
- if (scopedTheme !== maybeNewScopedTheme) {
16
- setScopedTheme(maybeNewScopedTheme)
17
- theme.set(UnistylesRuntime.getTheme(maybeNewScopedTheme))
14
+ // Follow the closest ScopedTheme, see useProxifiedUnistyles
15
+ if (scope && scopedTheme !== scope.name) {
16
+ setScopedTheme(scope.name as UnistylesTheme | undefined)
17
+ theme.set(UnistylesRuntime.getTheme(scope.name as UnistylesTheme | undefined))
18
18
  }
19
19
 
20
20
  useEffect(() => {
@@ -1,21 +1,21 @@
1
- import { useEffect, useState } from 'react'
1
+ import { useContext, useEffect, useState } from 'react'
2
2
  import { type SharedValue, useSharedValue } from 'react-native-reanimated'
3
3
 
4
4
  import type { UnistylesTheme } from '../types'
5
5
 
6
- import { UnistyleDependency, UnistylesRuntime, UnistylesShadowRegistry } from '../specs'
6
+ import { ScopedThemeContext } from '../components/ScopedThemeContext'
7
+ import { UnistyleDependency, UnistylesRuntime } from '../specs'
7
8
  import { services } from '../web/services'
8
9
 
9
10
  export const useAnimatedTheme = () => {
10
- const [scopedTheme, setScopedTheme] = useState(
11
- () => UnistylesShadowRegistry.getScopedTheme() as UnistylesTheme | undefined,
12
- )
11
+ const scope = useContext(ScopedThemeContext)
12
+ const [scopedTheme, setScopedTheme] = useState(scope?.name as UnistylesTheme | undefined)
13
13
  const theme = useSharedValue(UnistylesRuntime.getTheme(scopedTheme))
14
- const maybeNewScopedTheme = UnistylesShadowRegistry.getScopedTheme() as UnistylesTheme | undefined
15
14
 
16
- if (scopedTheme !== maybeNewScopedTheme) {
17
- setScopedTheme(maybeNewScopedTheme)
18
- theme.set(UnistylesRuntime.getTheme(maybeNewScopedTheme))
15
+ // Follow the closest ScopedTheme, see useProxifiedUnistyles
16
+ if (scope && scopedTheme !== scope.name) {
17
+ setScopedTheme(scope.name as UnistylesTheme | undefined)
18
+ theme.set(UnistylesRuntime.getTheme(scope.name as UnistylesTheme | undefined))
19
19
  }
20
20
 
21
21
  useEffect(() => {
@@ -1,5 +1,6 @@
1
1
  import { NitroModules } from 'react-native-nitro-modules'
2
2
 
3
+ import type { Scope } from '../../components/ScopedThemeContext'
3
4
  import type { VerifyReport } from '../../diagnostics/types'
4
5
  import type { UnistylesShadowRegistry as UnistylesShadowRegistrySpec } from './ShadowRegistry.nitro'
5
6
  import type { ShadowNode, Unistyle, ViewHandle } from './types'
@@ -8,7 +9,7 @@ import { StyleSheet } from '../StyleSheet'
8
9
 
9
10
  interface ShadowRegistry extends UnistylesShadowRegistrySpec {
10
11
  // Babel API
11
- add(handle?: ViewHandle, styles?: Array<Unistyle>): void
12
+ add(handle?: ViewHandle, styles?: Array<Unistyle>, scope?: Scope | null): void
12
13
  remove(handle?: ViewHandle): void
13
14
  // JSI
14
15
  link(node: ShadowNode, styles?: Array<Unistyle>): void
@@ -18,7 +19,7 @@ interface ShadowRegistry extends UnistylesShadowRegistrySpec {
18
19
  setScopedTheme(themeName?: string): void
19
20
  getScopedTheme(): string | undefined
20
21
  verify(): VerifyReport
21
- takeCommittedTags(): Array<number>
22
+ takeCommittedInstanceHandles(): Array<any>
22
23
  refreshReactNodes(nodes: Array<ShadowNode>): void
23
24
  }
24
25
 
@@ -72,137 +73,122 @@ const findShadowNodeForHandle = (handle: ViewHandle) => {
72
73
  return node
73
74
  }
74
75
 
75
- const getNativeTag = (handle: ViewHandle): number | undefined =>
76
- findFiberForHandle(handle)?.stateNode?.canonical?.nativeTag
77
-
78
- // by native tag, weak: views unmounted while frozen never unlink, their dead refs are dropped when the map doubles
79
- const linkedHandles = new Map<number, WeakRef<ViewHandle>>()
80
- const MIN_SWEEP_SIZE = 256
81
- let sweepSize = MIN_SWEEP_SIZE
82
-
83
- const sweepUnmountedHandles = () => {
84
- linkedHandles.forEach((ref, tag) => {
85
- if (!ref.deref()) {
86
- linkedHandles.delete(tag)
87
- }
88
- })
89
-
90
- sweepSize = Math.max(MIN_SWEEP_SIZE, linkedHandles.size * 2)
91
- }
92
-
93
- const trackHandle = (handle: ViewHandle) => {
94
- const tag = getNativeTag(handle)
95
-
96
- // views re-link on every render
97
- if (tag === undefined || linkedHandles.get(tag)?.deref() === handle) {
98
- return
99
- }
100
-
101
- linkedHandles.set(tag, new WeakRef(handle))
102
-
103
- if (linkedHandles.size >= sweepSize) {
104
- sweepUnmountedHandles()
105
- }
106
- }
107
-
108
- const untrackHandle = (handle: ViewHandle) => {
109
- const tag = getNativeTag(handle)
110
-
111
- if (tag !== undefined && linkedHandles.get(tag)?.deref() === handle) {
112
- linkedHandles.delete(tag)
113
- }
114
- }
115
-
116
76
  // React re-attaches an untouched subtree through its root's node and only follows clones made on the JS thread.
117
77
  // Once a node was cloned elsewhere (state updates, Reanimated), React would commit a subtree from before a Unistyles
118
78
  // update, so after a Unistyles commit the committed nodes and their host ancestors must point at the committed tree
119
79
  const refreshReactNodes = () => {
120
- const tags = HybridShadowRegistry.takeCommittedTags()
80
+ const fibers = HybridShadowRegistry.takeCommittedInstanceHandles()
121
81
 
122
- if (tags.length === 0) {
82
+ if (fibers.length === 0) {
123
83
  return
124
84
  }
125
85
 
126
- const nodes = new Set<ShadowNode>()
127
- const visited = new Set<any>()
86
+ const nodes: Array<ShadowNode> = []
87
+ const refreshedTags = new Set<number>()
88
+ const hostFibers: Array<any> = []
128
89
 
129
- for (const tag of tags) {
130
- const handle = linkedHandles.get(tag)?.deref()
131
-
132
- if (!handle) {
90
+ for (const fiber of fibers) {
91
+ // collected, unmounted since
92
+ if (!fiber) {
133
93
  continue
134
94
  }
135
95
 
136
- const fiber = findFiberForHandle(handle)
137
-
138
- // hidden by Suspense or a frozen screen
139
- if (!fiber || isInsideSuspendedBoundary(fiber)) {
140
- continue
141
- }
96
+ let isHidden = false
142
97
 
143
- for (let current: any = fiber; current && !visited.has(current); current = current.return) {
144
- visited.add(current)
98
+ hostFibers.length = 0
145
99
 
146
- if (current.alternate) {
147
- visited.add(current.alternate)
100
+ for (let current: any = fiber; current; current = current.return) {
101
+ // hidden by Suspense or a frozen screen
102
+ if ((current.tag === SUSPENSE_TAG || current.tag === OFFSCREEN_TAG) && current.memoizedState !== null) {
103
+ isHidden = true
148
104
  }
149
105
 
150
106
  if (current.tag !== HOST_COMPONENT_TAG) {
151
107
  continue
152
108
  }
153
109
 
154
- for (const target of [current, current.alternate]) {
155
- const node = target?.stateNode?.node
110
+ // its ancestors are visible and already collected
111
+ if (refreshedTags.has(current.stateNode?.canonical?.nativeTag)) {
112
+ break
113
+ }
114
+
115
+ hostFibers.push(current)
116
+ }
117
+
118
+ if (isHidden) {
119
+ continue
120
+ }
121
+
122
+ for (const hostFiber of hostFibers) {
123
+ const node = hostFiber.stateNode?.node
124
+ const alternateNode = hostFiber.alternate?.stateNode?.node
125
+ const hostTag = hostFiber.stateNode?.canonical?.nativeTag
126
+
127
+ if (hostTag !== undefined) {
128
+ refreshedTags.add(hostTag)
129
+ }
130
+
131
+ if (node) {
132
+ nodes.push(node)
133
+ }
156
134
 
157
- if (node) {
158
- nodes.add(node)
159
- }
135
+ if (alternateNode && alternateNode !== node) {
136
+ nodes.push(alternateNode)
160
137
  }
161
138
  }
162
139
  }
163
140
 
164
- if (nodes.size > 0) {
165
- HybridShadowRegistry.refreshReactNodes(Array.from(nodes))
141
+ if (nodes.length > 0) {
142
+ HybridShadowRegistry.refreshReactNodes(nodes)
166
143
  }
167
144
  }
168
145
 
169
146
  StyleSheet.addChangeListener(refreshReactNodes)
170
147
 
171
- // runs for every style on every link, so it allocates only for the rare merged style (eg. flattened by Animated)
172
- const splitMergedUnistyles = (style: any) => {
173
- let hasUnistyle = false
148
+ // skips empty styles and splits a style merged from many unistyles (eg. flattened by Animated) into one per unistyle
149
+ // runs for every style on every link, so it reads the keys once and allocates only for the rare merged style
150
+ const collectStyle = (style: any, collected: Array<any>) => {
151
+ if (!style) {
152
+ return
153
+ }
154
+
155
+ const keys = Object.keys(style)
156
+ let unistylesCount = 0
174
157
 
175
- for (const key in style) {
176
- if (!key.startsWith('unistyles_') || !Object.prototype.hasOwnProperty.call(style, key)) {
177
- continue
158
+ for (const key of keys) {
159
+ if (key.startsWith('unistyles_')) {
160
+ unistylesCount++
178
161
  }
162
+ }
179
163
 
180
- if (hasUnistyle) {
181
- return Object.keys(style)
182
- .filter((unistyleKey) => unistyleKey.startsWith('unistyles_'))
183
- .map((unistyleKey) => ({ [unistyleKey]: style[unistyleKey] }))
164
+ if (unistylesCount < 2) {
165
+ if (keys.length > 0) {
166
+ collected.push(style)
184
167
  }
185
168
 
186
- hasUnistyle = true
169
+ return
187
170
  }
188
171
 
189
- return style
172
+ for (const key of keys) {
173
+ if (key.startsWith('unistyles_')) {
174
+ collected.push({ [key]: style[key] })
175
+ }
176
+ }
190
177
  }
191
178
 
192
- HybridShadowRegistry.add = (handle, styles) => {
179
+ HybridShadowRegistry.add = (handle, styles, scope) => {
193
180
  // virtualized nodes can be null
194
181
  if (!handle || !styles) {
195
182
  return
196
183
  }
197
184
 
198
- const stylesArray = Array.isArray(styles) ? styles.flat() : [styles]
185
+ const filteredStyles: Array<any> = []
199
186
 
200
- // filter styles that are undefined or with no keys
201
- const filteredStyles = stylesArray
202
- .filter((style) => style && Object.keys(style).length > 0)
203
- .flat()
204
- .filter(Boolean)
205
- .flatMap(splitMergedUnistyles)
187
+ if (Array.isArray(styles)) {
188
+ styles.flat(Infinity).forEach((style) => collectStyle(style, filteredStyles))
189
+ } else {
190
+ collectStyle(styles, filteredStyles)
191
+ }
206
192
 
207
193
  if (filteredStyles.length > 0) {
208
194
  const node = findShadowNodeForHandle(handle)
@@ -213,8 +199,18 @@ HybridShadowRegistry.add = (handle, styles) => {
213
199
  )
214
200
  }
215
201
 
202
+ // the view can link after its ScopedTheme rendered, when the registry is already restored
203
+ const previousScopedTheme = scope ? HybridShadowRegistry.getScopedTheme() : undefined
204
+
205
+ if (scope) {
206
+ HybridShadowRegistry.setScopedTheme(scope.name)
207
+ }
208
+
216
209
  HybridShadowRegistry.link(node, filteredStyles)
217
- trackHandle(handle)
210
+
211
+ if (scope) {
212
+ HybridShadowRegistry.setScopedTheme(previousScopedTheme)
213
+ }
218
214
  }
219
215
  }
220
216
 
@@ -232,11 +228,17 @@ HybridShadowRegistry.remove = (handle) => {
232
228
  HybridShadowRegistry.suspend(maybeNode)
233
229
  } else {
234
230
  HybridShadowRegistry.unlink(maybeNode)
235
- untrackHandle(handle)
236
231
  }
237
232
  }
238
233
  }
239
234
 
240
- type PrivateMethods = 'add' | 'remove' | 'link' | 'unlink' | 'suspend' | 'takeCommittedTags' | 'refreshReactNodes'
235
+ type PrivateMethods =
236
+ | 'add'
237
+ | 'remove'
238
+ | 'link'
239
+ | 'unlink'
240
+ | 'suspend'
241
+ | 'takeCommittedInstanceHandles'
242
+ | 'refreshReactNodes'
241
243
 
242
244
  export const UnistylesShadowRegistry = HybridShadowRegistry as Omit<ShadowRegistry, PrivateMethods>
@@ -3,6 +3,31 @@ import type { CSSState } from './state'
3
3
  import { isPseudoClass } from '../convert/pseudo'
4
4
  import { getMediaQuery } from '../utils'
5
5
 
6
+ type SetProps = Parameters<CSSState['set']>[0]
7
+
8
+ // box-none and box-only aren't CSS values, polyfill them like react-native-web: [element, children]
9
+ const POINTER_EVENTS: Record<string, [string, string]> = {
10
+ 'box-none': ['none', 'auto'],
11
+ 'box-only': ['auto', 'none'],
12
+ }
13
+
14
+ export const getChildrenClassName = (className: string) => `:where(.${className})>*`
15
+
16
+ const setStyle = (state: CSSState, props: SetProps) => {
17
+ if (props.propertyKey !== 'pointerEvents') {
18
+ state.set(props)
19
+
20
+ return
21
+ }
22
+
23
+ const [value, childrenValue] = POINTER_EVENTS[props.value] ?? [props.value]
24
+
25
+ state.set({ ...props, value })
26
+ // :where() has no specificity, so a child's own pointerEvents always wins
27
+ // set it for every value, so re-applying the style can't leave a stale children rule
28
+ state.set({ ...props, className: getChildrenClassName(props.className), value: childrenValue })
29
+ }
30
+
6
31
  export const convertToCSS = (hash: string, value: Record<string, any>, state: CSSState) => {
7
32
  Object.entries(value).forEach(([styleKey, styleValue]) => {
8
33
  if (styleKey[0] === '_') {
@@ -15,7 +40,7 @@ export const convertToCSS = (hash: string, value: Record<string, any>, state: CS
15
40
  Object.entries(pseudoStyleValue).forEach(([breakpointStyleKey, breakpointStyleValue]) => {
16
41
  const mediaQuery = getMediaQuery(pseudoStyleKey, allBreakpoints)
17
42
 
18
- state.set({
43
+ setStyle(state, {
19
44
  mediaQuery,
20
45
  className: pseudoClassName,
21
46
  propertyKey: breakpointStyleKey,
@@ -26,7 +51,7 @@ export const convertToCSS = (hash: string, value: Record<string, any>, state: CS
26
51
  return
27
52
  }
28
53
 
29
- state.set({
54
+ setStyle(state, {
30
55
  className: pseudoClassName,
31
56
  propertyKey: pseudoStyleKey,
32
57
  value: pseudoStyleValue,
@@ -49,7 +74,7 @@ export const convertToCSS = (hash: string, value: Record<string, any>, state: CS
49
74
  .map(([key]) => key)
50
75
  const mediaQuery = getMediaQuery(styleKey, allBreakpoints)
51
76
 
52
- state.set({
77
+ setStyle(state, {
53
78
  mediaQuery,
54
79
  className: hash,
55
80
  propertyKey: breakpointStyleKey,
@@ -60,7 +85,7 @@ export const convertToCSS = (hash: string, value: Record<string, any>, state: CS
60
85
  return
61
86
  }
62
87
 
63
- state.set({
88
+ setStyle(state, {
64
89
  className: hash,
65
90
  propertyKey: styleKey,
66
91
  value: styleValue,
@@ -3,7 +3,7 @@ import type { UnistylesServices } from '../types'
3
3
 
4
4
  import { convertUnistyles } from '../convert'
5
5
  import { hyphenate, isServer } from '../utils'
6
- import { convertToCSS } from './core'
6
+ import { convertToCSS, getChildrenClassName } from './core'
7
7
 
8
8
  type MapType = Map<string, Map<string, Map<string, any>>>
9
9
  type SetProps = {
@@ -89,19 +89,31 @@ export class CSSState {
89
89
 
90
90
  Object.entries(values).forEach(([key, value]) => convertToCSS(key, value))
91
91
 
92
+ // ScopedTheme renders an element with data-unistyles-theme (or data-unistyles-reset for `reset`),
93
+ // so the variables follow the closest scope and styles don't need to know it while they render
92
94
  if (theme === 'light' || theme === 'dark') {
93
- this.themesCSS.set(`media ${theme}`, `@media (prefers-color-scheme: ${theme}){:root{${themeVars}}}`)
95
+ this.themesCSS.set(
96
+ `media ${theme}`,
97
+ `@media (prefers-color-scheme: ${theme}){:root,[data-unistyles-reset]{${themeVars}}}`,
98
+ )
94
99
  }
95
100
 
96
- this.themesCSS.set(theme, `:root.${theme}{${themeVars}}`)
101
+ this.themesCSS.set(
102
+ theme,
103
+ `:root.${theme},:root.${theme} [data-unistyles-reset],[data-unistyles-theme="${theme}"]{${themeVars}}`,
104
+ )
97
105
  }
98
106
 
99
107
  remove = (hash: string) => {
108
+ const childrenClassName = getChildrenClassName(hash)
109
+
100
110
  this.mainMap.forEach((styles) => {
101
111
  styles.delete(hash)
112
+ styles.delete(childrenClassName)
102
113
  })
103
114
  this.mqMap.forEach((styles) => {
104
115
  styles.delete(hash)
116
+ styles.delete(childrenClassName)
105
117
  })
106
118
  this.recreate()
107
119
  }
@@ -117,7 +129,7 @@ export class CSSState {
117
129
  }
118
130
 
119
131
  for (const [className, thirdLevelMap] of secondLevelMap) {
120
- styles += `.${className}{`
132
+ styles += className[0] === ':' ? `${className}{` : `.${className}{`
121
133
 
122
134
  for (const [propertyKey, value] of thirdLevelMap) {
123
135
  if (value === undefined) {
@@ -112,7 +112,7 @@ export class UnistylesRegistry {
112
112
 
113
113
  add = (value: UnistylesValues, forChild?: boolean) => {
114
114
  const generatedHash = generateHash(value)
115
- const hash = forChild ? `${generatedHash} > *` : generatedHash
115
+ const hash = forChild ? `${generatedHash}_child > *` : generatedHash
116
116
 
117
117
  if (!this.stylesCache.has(hash)) {
118
118
  this.applyStyles(hash, value)
@@ -101,7 +101,9 @@ export class UnistylesShadowRegistry {
101
101
  }
102
102
 
103
103
  setScopedTheme = (theme?: UnistylesTheme) => {
104
- this.scopedTheme = theme
104
+ // With CSS variables, the closest ScopedTheme element picks the theme, so styles are the same in every scope.
105
+ // Keeping no scope here also means a suspended ScopedTheme can't leave it behind
106
+ this.scopedTheme = this.services.state.CSSVars ? undefined : theme
105
107
  }
106
108
 
107
109
  getScopedTheme = () => this.scopedTheme