react-native-unistyles 3.5.2 → 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 (56) hide show
  1. package/android/src/main/java/com/unistyles/NativePlatform+android.kt +44 -7
  2. package/android/src/main/java/com/unistyles/NativePlatform+insets.kt +1 -1
  3. package/cxx/core/UnistylesRegistry.cpp +5 -5
  4. package/cxx/core/UnistylesRegistry.h +3 -3
  5. package/cxx/hybridObjects/HybridShadowRegistry.cpp +48 -14
  6. package/cxx/hybridObjects/HybridShadowRegistry.h +3 -2
  7. package/cxx/hybridObjects/HybridStyleSheet.cpp +3 -3
  8. package/cxx/parser/Parser.cpp +18 -2
  9. package/cxx/shadowTree/ShadowTreeManager.cpp +5 -5
  10. package/cxx/shadowTree/ShadowTreeManager.h +1 -1
  11. package/ios/NativePlatform+ios.swift +6 -4
  12. package/lib/commonjs/core/useProxifiedUnistyles/useProxifiedUnistyles.js +10 -0
  13. package/lib/commonjs/core/useProxifiedUnistyles/useProxifiedUnistyles.js.map +1 -1
  14. package/lib/commonjs/core/withUnistyles/withUnistyles.native.js +16 -15
  15. package/lib/commonjs/core/withUnistyles/withUnistyles.native.js.map +1 -1
  16. package/lib/commonjs/specs/ShadowRegistry/index.js +66 -73
  17. package/lib/commonjs/specs/ShadowRegistry/index.js.map +1 -1
  18. package/lib/commonjs/web/css/core.js +30 -5
  19. package/lib/commonjs/web/css/core.js.map +1 -1
  20. package/lib/commonjs/web/css/state.js +4 -1
  21. package/lib/commonjs/web/css/state.js.map +1 -1
  22. package/lib/commonjs/web/registry.js +1 -1
  23. package/lib/commonjs/web/registry.js.map +1 -1
  24. package/lib/module/core/useProxifiedUnistyles/useProxifiedUnistyles.js +10 -0
  25. package/lib/module/core/useProxifiedUnistyles/useProxifiedUnistyles.js.map +1 -1
  26. package/lib/module/core/withUnistyles/withUnistyles.native.js +14 -14
  27. package/lib/module/core/withUnistyles/withUnistyles.native.js.map +1 -1
  28. package/lib/module/specs/ShadowRegistry/index.js +66 -73
  29. package/lib/module/specs/ShadowRegistry/index.js.map +1 -1
  30. package/lib/module/web/css/core.js +28 -4
  31. package/lib/module/web/css/core.js.map +1 -1
  32. package/lib/module/web/css/state.js +5 -2
  33. package/lib/module/web/css/state.js.map +1 -1
  34. package/lib/module/web/registry.js +1 -1
  35. package/lib/module/web/registry.js.map +1 -1
  36. package/lib/typescript/src/__tests__/web-child-selector.spec.d.ts +2 -0
  37. package/lib/typescript/src/__tests__/web-child-selector.spec.d.ts.map +1 -0
  38. package/lib/typescript/src/__tests__/web-pointer-events.spec.d.ts +2 -0
  39. package/lib/typescript/src/__tests__/web-pointer-events.spec.d.ts.map +1 -0
  40. package/lib/typescript/src/__tests__/withUnistyles.spec.d.ts +2 -0
  41. package/lib/typescript/src/__tests__/withUnistyles.spec.d.ts.map +1 -0
  42. package/lib/typescript/src/core/useProxifiedUnistyles/useProxifiedUnistyles.d.ts.map +1 -1
  43. package/lib/typescript/src/core/withUnistyles/withUnistyles.native.d.ts +6 -0
  44. package/lib/typescript/src/core/withUnistyles/withUnistyles.native.d.ts.map +1 -1
  45. package/lib/typescript/src/specs/ShadowRegistry/index.d.ts +2 -2
  46. package/lib/typescript/src/specs/ShadowRegistry/index.d.ts.map +1 -1
  47. package/lib/typescript/src/web/css/core.d.ts +1 -0
  48. package/lib/typescript/src/web/css/core.d.ts.map +1 -1
  49. package/lib/typescript/src/web/css/state.d.ts.map +1 -1
  50. package/package.json +1 -1
  51. package/src/core/useProxifiedUnistyles/useProxifiedUnistyles.ts +11 -0
  52. package/src/core/withUnistyles/withUnistyles.native.tsx +22 -25
  53. package/src/specs/ShadowRegistry/index.ts +79 -89
  54. package/src/web/css/core.ts +29 -4
  55. package/src/web/css/state.ts +6 -2
  56. package/src/web/registry.ts +1 -1
@@ -31,6 +31,28 @@ type MappedSecrets = {
31
31
  dependencies: Array<UnistyleDependency>
32
32
  }
33
33
 
34
+ export const getSecrets = (styleProps: Record<string, any> = {}): MappedSecrets => {
35
+ const styles = Array.isArray(styleProps) ? styleProps.flat(Infinity) : [styleProps]
36
+
37
+ const secrets: Array<UnistylesSecrets> = styles.filter(Boolean).reduce((acc, style) => {
38
+ const unistyleKey = Object.keys(style).find((key) => key.startsWith('unistyles_'))
39
+
40
+ return acc.concat([
41
+ unistyleKey
42
+ ? style[unistyleKey]
43
+ : {
44
+ uni__getStyles: () => style,
45
+ uni__dependencies: [],
46
+ },
47
+ ])
48
+ }, [])
49
+
50
+ return {
51
+ styles: secrets.reduce((acc, secret) => Object.assign(acc, secret.uni__getStyles()), {} as Record<string, any>),
52
+ dependencies: secrets.flatMap((secret) => secret.uni__dependencies),
53
+ }
54
+ }
55
+
34
56
  export const withUnistyles = <TComponent, TMappings extends GenericComponentProps<TComponent>>(
35
57
  Component: TComponent,
36
58
  mappings?: Mappings<TMappings>,
@@ -44,31 +66,6 @@ export const withUnistyles = <TComponent, TMappings extends GenericComponentProp
44
66
  contentContainerStyle?: UnistylesValues
45
67
  }
46
68
 
47
- const getSecrets = (styleProps: Record<string, any> = {}): MappedSecrets => {
48
- const styles = Array.isArray(styleProps) ? styleProps.flat() : [styleProps]
49
-
50
- const secrets: Array<UnistylesSecrets> = styles.filter(Boolean).reduce((acc, style) => {
51
- const unistyleKey = Object.keys(style).find((key) => key.startsWith('unistyles_'))
52
-
53
- return acc.concat([
54
- unistyleKey
55
- ? style[unistyleKey]
56
- : {
57
- uni__getStyles: () => style,
58
- uni__dependencies: [],
59
- },
60
- ])
61
- }, [])
62
-
63
- return {
64
- styles: secrets.reduce(
65
- (acc, secret) => Object.assign(acc, secret.uni__getStyles()),
66
- {} as Record<string, any>,
67
- ),
68
- dependencies: secrets.flatMap((secret) => secret.uni__dependencies),
69
- }
70
- }
71
-
72
69
  return forwardRef<GenericComponentRef<TComponent>, PropsWithUnistyles>((props, ref) => {
73
70
  const narrowedProps = props as PropsWithUnistyles & UnistyleStyles
74
71
  const NativeComponent = Component as ComponentType
@@ -19,7 +19,7 @@ interface ShadowRegistry extends UnistylesShadowRegistrySpec {
19
19
  setScopedTheme(themeName?: string): void
20
20
  getScopedTheme(): string | undefined
21
21
  verify(): VerifyReport
22
- takeCommittedTags(): Array<number>
22
+ takeCommittedInstanceHandles(): Array<any>
23
23
  refreshReactNodes(nodes: Array<ShadowNode>): void
24
24
  }
25
25
 
@@ -73,121 +73,107 @@ const findShadowNodeForHandle = (handle: ViewHandle) => {
73
73
  return node
74
74
  }
75
75
 
76
- const getNativeTag = (handle: ViewHandle): number | undefined =>
77
- findFiberForHandle(handle)?.stateNode?.canonical?.nativeTag
78
-
79
- // by native tag, weak: views unmounted while frozen never unlink, their dead refs are dropped when the map doubles
80
- const linkedHandles = new Map<number, WeakRef<ViewHandle>>()
81
- const MIN_SWEEP_SIZE = 256
82
- let sweepSize = MIN_SWEEP_SIZE
83
-
84
- const sweepUnmountedHandles = () => {
85
- linkedHandles.forEach((ref, tag) => {
86
- if (!ref.deref()) {
87
- linkedHandles.delete(tag)
88
- }
89
- })
90
-
91
- sweepSize = Math.max(MIN_SWEEP_SIZE, linkedHandles.size * 2)
92
- }
93
-
94
- const trackHandle = (handle: ViewHandle) => {
95
- const tag = getNativeTag(handle)
96
-
97
- // views re-link on every render
98
- if (tag === undefined || linkedHandles.get(tag)?.deref() === handle) {
99
- return
100
- }
101
-
102
- linkedHandles.set(tag, new WeakRef(handle))
103
-
104
- if (linkedHandles.size >= sweepSize) {
105
- sweepUnmountedHandles()
106
- }
107
- }
108
-
109
- const untrackHandle = (handle: ViewHandle) => {
110
- const tag = getNativeTag(handle)
111
-
112
- if (tag !== undefined && linkedHandles.get(tag)?.deref() === handle) {
113
- linkedHandles.delete(tag)
114
- }
115
- }
116
-
117
76
  // React re-attaches an untouched subtree through its root's node and only follows clones made on the JS thread.
118
77
  // Once a node was cloned elsewhere (state updates, Reanimated), React would commit a subtree from before a Unistyles
119
78
  // update, so after a Unistyles commit the committed nodes and their host ancestors must point at the committed tree
120
79
  const refreshReactNodes = () => {
121
- const tags = HybridShadowRegistry.takeCommittedTags()
80
+ const fibers = HybridShadowRegistry.takeCommittedInstanceHandles()
122
81
 
123
- if (tags.length === 0) {
82
+ if (fibers.length === 0) {
124
83
  return
125
84
  }
126
85
 
127
- const nodes = new Set<ShadowNode>()
128
- const visited = new Set<any>()
129
-
130
- for (const tag of tags) {
131
- const handle = linkedHandles.get(tag)?.deref()
86
+ const nodes: Array<ShadowNode> = []
87
+ const refreshedTags = new Set<number>()
88
+ const hostFibers: Array<any> = []
132
89
 
133
- if (!handle) {
90
+ for (const fiber of fibers) {
91
+ // collected, unmounted since
92
+ if (!fiber) {
134
93
  continue
135
94
  }
136
95
 
137
- const fiber = findFiberForHandle(handle)
138
-
139
- // hidden by Suspense or a frozen screen
140
- if (!fiber || isInsideSuspendedBoundary(fiber)) {
141
- continue
142
- }
96
+ let isHidden = false
143
97
 
144
- for (let current: any = fiber; current && !visited.has(current); current = current.return) {
145
- visited.add(current)
98
+ hostFibers.length = 0
146
99
 
147
- if (current.alternate) {
148
- 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
149
104
  }
150
105
 
151
106
  if (current.tag !== HOST_COMPONENT_TAG) {
152
107
  continue
153
108
  }
154
109
 
155
- for (const target of [current, current.alternate]) {
156
- 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
+ }
157
134
 
158
- if (node) {
159
- nodes.add(node)
160
- }
135
+ if (alternateNode && alternateNode !== node) {
136
+ nodes.push(alternateNode)
161
137
  }
162
138
  }
163
139
  }
164
140
 
165
- if (nodes.size > 0) {
166
- HybridShadowRegistry.refreshReactNodes(Array.from(nodes))
141
+ if (nodes.length > 0) {
142
+ HybridShadowRegistry.refreshReactNodes(nodes)
167
143
  }
168
144
  }
169
145
 
170
146
  StyleSheet.addChangeListener(refreshReactNodes)
171
147
 
172
- // runs for every style on every link, so it allocates only for the rare merged style (eg. flattened by Animated)
173
- const splitMergedUnistyles = (style: any) => {
174
- 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
175
157
 
176
- for (const key in style) {
177
- if (!key.startsWith('unistyles_') || !Object.prototype.hasOwnProperty.call(style, key)) {
178
- continue
158
+ for (const key of keys) {
159
+ if (key.startsWith('unistyles_')) {
160
+ unistylesCount++
179
161
  }
162
+ }
180
163
 
181
- if (hasUnistyle) {
182
- return Object.keys(style)
183
- .filter((unistyleKey) => unistyleKey.startsWith('unistyles_'))
184
- .map((unistyleKey) => ({ [unistyleKey]: style[unistyleKey] }))
164
+ if (unistylesCount < 2) {
165
+ if (keys.length > 0) {
166
+ collected.push(style)
185
167
  }
186
168
 
187
- hasUnistyle = true
169
+ return
188
170
  }
189
171
 
190
- return style
172
+ for (const key of keys) {
173
+ if (key.startsWith('unistyles_')) {
174
+ collected.push({ [key]: style[key] })
175
+ }
176
+ }
191
177
  }
192
178
 
193
179
  HybridShadowRegistry.add = (handle, styles, scope) => {
@@ -196,13 +182,13 @@ HybridShadowRegistry.add = (handle, styles, scope) => {
196
182
  return
197
183
  }
198
184
 
199
- const stylesArray = Array.isArray(styles) ? styles.flat(Infinity) : [styles]
185
+ const filteredStyles: Array<any> = []
200
186
 
201
- // filter styles that are undefined or with no keys
202
- const filteredStyles = stylesArray
203
- .filter((style) => style && Object.keys(style).length > 0)
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)
@@ -225,8 +211,6 @@ HybridShadowRegistry.add = (handle, styles, scope) => {
225
211
  if (scope) {
226
212
  HybridShadowRegistry.setScopedTheme(previousScopedTheme)
227
213
  }
228
-
229
- trackHandle(handle)
230
214
  }
231
215
  }
232
216
 
@@ -244,11 +228,17 @@ HybridShadowRegistry.remove = (handle) => {
244
228
  HybridShadowRegistry.suspend(maybeNode)
245
229
  } else {
246
230
  HybridShadowRegistry.unlink(maybeNode)
247
- untrackHandle(handle)
248
231
  }
249
232
  }
250
233
  }
251
234
 
252
- 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'
253
243
 
254
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 = {
@@ -105,11 +105,15 @@ export class CSSState {
105
105
  }
106
106
 
107
107
  remove = (hash: string) => {
108
+ const childrenClassName = getChildrenClassName(hash)
109
+
108
110
  this.mainMap.forEach((styles) => {
109
111
  styles.delete(hash)
112
+ styles.delete(childrenClassName)
110
113
  })
111
114
  this.mqMap.forEach((styles) => {
112
115
  styles.delete(hash)
116
+ styles.delete(childrenClassName)
113
117
  })
114
118
  this.recreate()
115
119
  }
@@ -125,7 +129,7 @@ export class CSSState {
125
129
  }
126
130
 
127
131
  for (const [className, thirdLevelMap] of secondLevelMap) {
128
- styles += `.${className}{`
132
+ styles += className[0] === ':' ? `${className}{` : `.${className}{`
129
133
 
130
134
  for (const [propertyKey, value] of thirdLevelMap) {
131
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)