react-native-unistyles 3.0.0-alpha.35 → 3.0.0-alpha.36

Sign up to get free protection for your applications and to get access to all the features.
Files changed (199) hide show
  1. package/android/CMakeLists.txt +31 -36
  2. package/android/build.gradle +2 -1
  3. package/android/src/main/cxx/NativeUnistylesModule.cpp +71 -0
  4. package/android/src/main/cxx/NativeUnistylesModule.h +42 -0
  5. package/android/src/main/cxx/cpp-adapter.cpp +8 -86
  6. package/android/src/main/java/com/unistyles/NativePlatform.kt +184 -0
  7. package/android/src/main/java/com/unistyles/UnistylesModule+insets.kt +8 -0
  8. package/android/src/main/java/com/unistyles/UnistylesModule.kt +47 -0
  9. package/android/src/main/java/com/unistyles/UnistylesPackage.kt +16 -14
  10. package/cxx/NativePlatform.h +11 -0
  11. package/cxx/common/Helpers.h +1 -1
  12. package/cxx/core/Unistyle.h +1 -1
  13. package/cxx/core/UnistylesRegistry.h +1 -1
  14. package/cxx/hybridObjects/HybridNavigationBar.h +3 -3
  15. package/cxx/hybridObjects/HybridStatusBar.h +3 -3
  16. package/cxx/hybridObjects/HybridUnistylesRuntime.cpp +4 -4
  17. package/cxx/hybridObjects/HybridUnistylesRuntime.h +2 -2
  18. package/lib/commonjs/components/useMedia.js.map +1 -1
  19. package/lib/commonjs/components/useMedia.web.js +43 -0
  20. package/lib/commonjs/components/useMedia.web.js.map +1 -0
  21. package/lib/commonjs/core/createUnistylesComponent.js +57 -4
  22. package/lib/commonjs/core/createUnistylesComponent.js.map +1 -1
  23. package/lib/commonjs/core/createUnistylesComponent.native.js +6 -5
  24. package/lib/commonjs/core/createUnistylesComponent.native.js.map +1 -1
  25. package/lib/commonjs/web/convert/index.js +16 -11
  26. package/lib/commonjs/web/convert/index.js.map +1 -1
  27. package/lib/commonjs/web/convert/object/boxShadow.js +58 -0
  28. package/lib/commonjs/web/convert/object/boxShadow.js.map +1 -0
  29. package/lib/commonjs/web/convert/object/filter.js +42 -0
  30. package/lib/commonjs/web/convert/object/filter.js.map +1 -0
  31. package/lib/commonjs/web/convert/object/index.js +39 -0
  32. package/lib/commonjs/web/convert/object/index.js.map +1 -0
  33. package/lib/commonjs/web/convert/object/objectStyle.js +55 -0
  34. package/lib/commonjs/web/convert/object/objectStyle.js.map +1 -0
  35. package/lib/commonjs/web/convert/object/transform.js +27 -0
  36. package/lib/commonjs/web/convert/object/transform.js.map +1 -0
  37. package/lib/commonjs/web/convert/{boxShadow.js → shadow/boxShadow.js} +9 -24
  38. package/lib/commonjs/web/convert/shadow/boxShadow.js.map +1 -0
  39. package/lib/commonjs/web/convert/shadow/getShadowBreakpoints.js +38 -0
  40. package/lib/commonjs/web/convert/shadow/getShadowBreakpoints.js.map +1 -0
  41. package/lib/commonjs/web/convert/shadow/index.js +28 -0
  42. package/lib/commonjs/web/convert/shadow/index.js.map +1 -0
  43. package/lib/commonjs/web/convert/{textShadow.js → shadow/textShadow.js} +9 -23
  44. package/lib/commonjs/web/convert/shadow/textShadow.js.map +1 -0
  45. package/lib/commonjs/web/convert/utils.js +7 -3
  46. package/lib/commonjs/web/convert/utils.js.map +1 -1
  47. package/lib/commonjs/web/listener.js +10 -0
  48. package/lib/commonjs/web/listener.js.map +1 -1
  49. package/lib/commonjs/web/registry.js +13 -26
  50. package/lib/commonjs/web/registry.js.map +1 -1
  51. package/lib/commonjs/web/runtime.js +3 -0
  52. package/lib/commonjs/web/runtime.js.map +1 -1
  53. package/lib/commonjs/web/shadowRegistry.js +79 -39
  54. package/lib/commonjs/web/shadowRegistry.js.map +1 -1
  55. package/lib/commonjs/web/utils/common.js +3 -33
  56. package/lib/commonjs/web/utils/common.js.map +1 -1
  57. package/lib/commonjs/web/utils/unistyle.js +4 -1
  58. package/lib/commonjs/web/utils/unistyle.js.map +1 -1
  59. package/lib/module/components/useMedia.js.map +1 -1
  60. package/lib/module/components/useMedia.web.js +38 -0
  61. package/lib/module/components/useMedia.web.js.map +1 -0
  62. package/lib/module/core/createUnistylesComponent.js +55 -3
  63. package/lib/module/core/createUnistylesComponent.js.map +1 -1
  64. package/lib/module/core/createUnistylesComponent.native.js +6 -5
  65. package/lib/module/core/createUnistylesComponent.native.js.map +1 -1
  66. package/lib/module/web/convert/index.js +14 -9
  67. package/lib/module/web/convert/index.js.map +1 -1
  68. package/lib/module/web/convert/object/boxShadow.js +53 -0
  69. package/lib/module/web/convert/object/boxShadow.js.map +1 -0
  70. package/lib/module/web/convert/object/filter.js +37 -0
  71. package/lib/module/web/convert/object/filter.js.map +1 -0
  72. package/lib/module/web/convert/object/index.js +6 -0
  73. package/lib/module/web/convert/object/index.js.map +1 -0
  74. package/lib/module/web/convert/object/objectStyle.js +50 -0
  75. package/lib/module/web/convert/object/objectStyle.js.map +1 -0
  76. package/lib/module/web/convert/object/transform.js +22 -0
  77. package/lib/module/web/convert/object/transform.js.map +1 -0
  78. package/lib/module/web/convert/{boxShadow.js → shadow/boxShadow.js} +9 -24
  79. package/lib/module/web/convert/shadow/boxShadow.js.map +1 -0
  80. package/lib/module/web/convert/shadow/getShadowBreakpoints.js +33 -0
  81. package/lib/module/web/convert/shadow/getShadowBreakpoints.js.map +1 -0
  82. package/lib/module/web/convert/shadow/index.js +5 -0
  83. package/lib/module/web/convert/shadow/index.js.map +1 -0
  84. package/lib/module/web/convert/{textShadow.js → shadow/textShadow.js} +9 -23
  85. package/lib/module/web/convert/shadow/textShadow.js.map +1 -0
  86. package/lib/module/web/convert/utils.js +4 -2
  87. package/lib/module/web/convert/utils.js.map +1 -1
  88. package/lib/module/web/listener.js +10 -0
  89. package/lib/module/web/listener.js.map +1 -1
  90. package/lib/module/web/registry.js +14 -27
  91. package/lib/module/web/registry.js.map +1 -1
  92. package/lib/module/web/runtime.js +3 -0
  93. package/lib/module/web/runtime.js.map +1 -1
  94. package/lib/module/web/shadowRegistry.js +80 -40
  95. package/lib/module/web/shadowRegistry.js.map +1 -1
  96. package/lib/module/web/utils/common.js +1 -31
  97. package/lib/module/web/utils/common.js.map +1 -1
  98. package/lib/module/web/utils/unistyle.js +4 -1
  99. package/lib/module/web/utils/unistyle.js.map +1 -1
  100. package/lib/typescript/src/components/useMedia.web.d.ts +6 -0
  101. package/lib/typescript/src/components/useMedia.web.d.ts.map +1 -0
  102. package/lib/typescript/src/core/createUnistylesComponent.d.ts +4 -3
  103. package/lib/typescript/src/core/createUnistylesComponent.d.ts.map +1 -1
  104. package/lib/typescript/src/core/createUnistylesComponent.native.d.ts +4 -3
  105. package/lib/typescript/src/core/createUnistylesComponent.native.d.ts.map +1 -1
  106. package/lib/typescript/src/types/common.d.ts +1 -0
  107. package/lib/typescript/src/types/common.d.ts.map +1 -1
  108. package/lib/typescript/src/web/convert/index.d.ts.map +1 -1
  109. package/lib/typescript/src/web/convert/object/boxShadow.d.ts +9 -0
  110. package/lib/typescript/src/web/convert/object/boxShadow.d.ts.map +1 -0
  111. package/lib/typescript/src/web/convert/object/filter.d.ts +3 -0
  112. package/lib/typescript/src/web/convert/object/filter.d.ts.map +1 -0
  113. package/lib/typescript/src/web/convert/object/index.d.ts +4 -0
  114. package/lib/typescript/src/web/convert/object/index.d.ts.map +1 -0
  115. package/lib/typescript/src/web/convert/object/objectStyle.d.ts +5 -0
  116. package/lib/typescript/src/web/convert/object/objectStyle.d.ts.map +1 -0
  117. package/lib/typescript/src/web/convert/object/transform.d.ts +3 -0
  118. package/lib/typescript/src/web/convert/object/transform.d.ts.map +1 -0
  119. package/lib/typescript/src/web/convert/shadow/boxShadow.d.ts.map +1 -0
  120. package/lib/typescript/src/web/convert/shadow/getShadowBreakpoints.d.ts +2 -0
  121. package/lib/typescript/src/web/convert/shadow/getShadowBreakpoints.d.ts.map +1 -0
  122. package/lib/typescript/src/web/convert/shadow/index.d.ts +3 -0
  123. package/lib/typescript/src/web/convert/shadow/index.d.ts.map +1 -0
  124. package/lib/typescript/src/web/convert/shadow/textShadow.d.ts.map +1 -0
  125. package/lib/typescript/src/web/convert/types.d.ts +7 -1
  126. package/lib/typescript/src/web/convert/types.d.ts.map +1 -1
  127. package/lib/typescript/src/web/convert/utils.d.ts +8 -4
  128. package/lib/typescript/src/web/convert/utils.d.ts.map +1 -1
  129. package/lib/typescript/src/web/create.d.ts +8 -8
  130. package/lib/typescript/src/web/index.d.ts +8 -8
  131. package/lib/typescript/src/web/listener.d.ts +2 -0
  132. package/lib/typescript/src/web/listener.d.ts.map +1 -1
  133. package/lib/typescript/src/web/registry.d.ts +5 -9
  134. package/lib/typescript/src/web/registry.d.ts.map +1 -1
  135. package/lib/typescript/src/web/runtime.d.ts +1 -0
  136. package/lib/typescript/src/web/runtime.d.ts.map +1 -1
  137. package/lib/typescript/src/web/shadowRegistry.d.ts +4 -1
  138. package/lib/typescript/src/web/shadowRegistry.d.ts.map +1 -1
  139. package/lib/typescript/src/web/utils/common.d.ts +1 -6
  140. package/lib/typescript/src/web/utils/common.d.ts.map +1 -1
  141. package/lib/typescript/src/web/utils/unistyle.d.ts.map +1 -1
  142. package/nitrogen/generated/android/c++/JHybridNativePlatformSpec.hpp +6 -0
  143. package/nitrogen/generated/android/kotlin/com/margelo/nitro/unistyles/Dimensions.kt +1 -0
  144. package/nitrogen/generated/android/kotlin/com/margelo/nitro/unistyles/Func_void.kt +1 -0
  145. package/nitrogen/generated/android/kotlin/com/margelo/nitro/unistyles/Func_void_std__vector_UnistyleDependency_.kt +1 -0
  146. package/nitrogen/generated/android/kotlin/com/margelo/nitro/unistyles/Insets.kt +1 -0
  147. package/nitrogen/generated/android/kotlin/com/margelo/nitro/unistyles/UnistylesNativeMiniRuntime.kt +1 -0
  148. package/nitrogen/generated/android/unistyles+autolinking.gradle +2 -0
  149. package/nitrogen/generated/ios/Unistyles+autolinking.rb +1 -1
  150. package/package.json +3 -3
  151. package/src/components/useMedia.ts +1 -1
  152. package/src/components/useMedia.web.ts +47 -0
  153. package/src/core/createUnistylesComponent.native.tsx +12 -10
  154. package/src/core/createUnistylesComponent.tsx +72 -6
  155. package/src/types/common.ts +1 -0
  156. package/src/web/convert/index.ts +16 -9
  157. package/src/web/convert/object/boxShadow.ts +54 -0
  158. package/src/web/convert/object/filter.ts +39 -0
  159. package/src/web/convert/object/index.ts +3 -0
  160. package/src/web/convert/object/objectStyle.ts +68 -0
  161. package/src/web/convert/object/transform.ts +24 -0
  162. package/src/web/convert/{boxShadow.ts → shadow/boxShadow.ts} +9 -30
  163. package/src/web/convert/shadow/getShadowBreakpoints.ts +34 -0
  164. package/src/web/convert/shadow/index.ts +2 -0
  165. package/src/web/convert/{textShadow.ts → shadow/textShadow.ts} +9 -29
  166. package/src/web/convert/types.ts +8 -1
  167. package/src/web/convert/utils.ts +11 -5
  168. package/src/web/listener.ts +10 -0
  169. package/src/web/registry.ts +10 -31
  170. package/src/web/runtime.ts +4 -0
  171. package/src/web/shadowRegistry.ts +85 -46
  172. package/src/web/utils/common.ts +1 -37
  173. package/src/web/utils/unistyle.ts +5 -1
  174. package/android/src/main/cxx/helpers.cpp +0 -105
  175. package/android/src/main/cxx/helpers.h +0 -16
  176. package/android/src/main/cxx/platform.cpp +0 -170
  177. package/android/src/main/cxx/platform.h +0 -20
  178. package/lib/commonjs/web/convert/boxShadow.js.map +0 -1
  179. package/lib/commonjs/web/convert/shadow.js +0 -68
  180. package/lib/commonjs/web/convert/shadow.js.map +0 -1
  181. package/lib/commonjs/web/convert/textShadow.js.map +0 -1
  182. package/lib/commonjs/web/convert/transform.js +0 -72
  183. package/lib/commonjs/web/convert/transform.js.map +0 -1
  184. package/lib/module/web/convert/boxShadow.js.map +0 -1
  185. package/lib/module/web/convert/shadow.js +0 -63
  186. package/lib/module/web/convert/shadow.js.map +0 -1
  187. package/lib/module/web/convert/textShadow.js.map +0 -1
  188. package/lib/module/web/convert/transform.js +0 -67
  189. package/lib/module/web/convert/transform.js.map +0 -1
  190. package/lib/typescript/src/web/convert/boxShadow.d.ts.map +0 -1
  191. package/lib/typescript/src/web/convert/shadow.d.ts +0 -2
  192. package/lib/typescript/src/web/convert/shadow.d.ts.map +0 -1
  193. package/lib/typescript/src/web/convert/textShadow.d.ts.map +0 -1
  194. package/lib/typescript/src/web/convert/transform.d.ts +0 -4
  195. package/lib/typescript/src/web/convert/transform.d.ts.map +0 -1
  196. package/src/web/convert/shadow.ts +0 -68
  197. package/src/web/convert/transform.ts +0 -88
  198. /package/lib/typescript/src/web/convert/{boxShadow.d.ts → shadow/boxShadow.d.ts} +0 -0
  199. /package/lib/typescript/src/web/convert/{textShadow.d.ts → shadow/textShadow.d.ts} +0 -0
@@ -20,7 +20,7 @@
20
20
  # ```
21
21
 
22
22
  def add_nitrogen_files(spec)
23
- Pod::UI.puts "[NitroModules] Adding Unistyles specs..."
23
+ Pod::UI.puts "[NitroModules] 🔥 Unistyles is boosted by nitro!"
24
24
 
25
25
  spec.dependency "NitroModules"
26
26
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-unistyles",
3
- "version": "3.0.0-alpha.35",
3
+ "version": "3.0.0-alpha.36",
4
4
  "description": "Level up your React Native StyleSheet",
5
5
  "scripts": {
6
6
  "test": "jest",
@@ -74,11 +74,11 @@
74
74
  "husky": "9.1.6",
75
75
  "jest": "29.7.0",
76
76
  "metro-react-native-babel-preset": "0.77.0",
77
- "nitro-codegen": "0.14.0",
77
+ "nitro-codegen": "0.15.0",
78
78
  "react": "18.3.1",
79
79
  "react-native": "0.76.0",
80
80
  "react-native-builder-bob": "0.30.2",
81
- "react-native-nitro-modules": "0.14.0",
81
+ "react-native-nitro-modules": "0.15.0",
82
82
  "react-test-renderer": "18.3.1",
83
83
  "release-it": "17.10.0",
84
84
  "typescript": "5.6.3"
@@ -12,7 +12,7 @@ export const useMedia = (config: { mq: symbol }) => {
12
12
  return false
13
13
  }
14
14
 
15
- const parsedMq = parseMq(maybeMq)
15
+ const parsedMq = parseMq(maybeMq)
16
16
 
17
17
  if (!isValidMq(parsedMq)) {
18
18
  console.error(`🦄 Unistyles: Received invalid mq where min is greater than max: ${maybeMq}`)
@@ -0,0 +1,47 @@
1
+ import { useEffect, useRef, useState } from 'react'
2
+ import { isUnistylesMq, isValidMq, parseMq } from '../mq'
3
+
4
+ export const useMedia = (config: { mq: symbol }) => {
5
+ const disposeRef = useRef(() => {})
6
+ const [isVisible, setIsVisible] = useState(() => {
7
+ const maybeMq = config.mq as unknown as string
8
+
9
+ if (!isUnistylesMq(maybeMq)) {
10
+ console.error(`🦄 Unistyles: Received invalid mq: ${maybeMq}`)
11
+
12
+ return false
13
+ }
14
+
15
+ const parsedMq = parseMq(maybeMq)
16
+
17
+ if (!isValidMq(parsedMq)) {
18
+ console.error(`🦄 Unistyles: Received invalid mq where min is greater than max: ${maybeMq}`)
19
+
20
+ return false
21
+ }
22
+
23
+ const { minWidth, maxWidth, minHeight, maxHeight } = parsedMq
24
+
25
+ const mediaQuery = [
26
+ minWidth !== undefined ? `(min-width: ${minWidth}px)` : undefined,
27
+ maxWidth !== undefined ? `(max-width: ${maxWidth}px)` : undefined,
28
+ minHeight !== undefined ? `(min-height: ${minHeight}px)` : undefined,
29
+ maxHeight !== undefined ? `(max-height: ${maxHeight}px)` : undefined
30
+ ].filter(Boolean).join(' and ')
31
+
32
+ const media = window.matchMedia(mediaQuery)
33
+ const handler = (event: MediaQueryListEvent) => setIsVisible(event.matches)
34
+
35
+ media.addEventListener('change', handler)
36
+ disposeRef.current = () => media.removeEventListener('change', handler)
37
+
38
+ return media.matches
39
+ })
40
+
41
+ // Unmount
42
+ useEffect(() => () => disposeRef.current(), [])
43
+
44
+ return {
45
+ isVisible
46
+ }
47
+ }
@@ -1,13 +1,13 @@
1
- import React, { type ComponentProps, type ComponentType, useEffect, useRef, useState } from 'react'
1
+ import React, { type ComponentType, useEffect, useRef, useState } from 'react'
2
2
  import type { UnistylesTheme } from '../types'
3
3
  import { StyleSheet, UnistyleDependency, UnistylesRuntime, type UnistylesStyleSheet } from '../specs'
4
-
5
- type Mappings<T extends ComponentType<any>> = (theme: UnistylesTheme) => Partial<Omit<ComponentProps<T>, typeof SUPPORTED_STYLE_PROPS[number]>>
4
+ import type { PartialBy } from '../types/common'
6
5
 
7
6
  const SUPPORTED_STYLE_PROPS = ['style', 'contentContainerStyle'] as const
7
+ type SupportedStyleProps = typeof SUPPORTED_STYLE_PROPS[number]
8
8
 
9
- export const createUnistylesComponent = <T extends ComponentType<any>>(Component: T, mappings: Mappings<T> = () => ({})) => {
10
- return (props: ComponentProps<T>) => {
9
+ export const createUnistylesComponent =<TProps extends Record<string, any>, TMappings extends Partial<Omit<TProps, SupportedStyleProps>>>(Component: ComponentType<TProps>, mappings?: (theme: UnistylesTheme) => TMappings) => {
10
+ return (props: PartialBy<TProps, keyof TMappings | SupportedStyleProps>) => {
11
11
  const [theme, setTheme] = useState<UnistylesTheme>(UnistylesRuntime.getTheme())
12
12
  const [, setRt] = useState(0)
13
13
  const stylesRef = useRef<Record<string, any>>({})
@@ -20,6 +20,7 @@ export const createUnistylesComponent = <T extends ComponentType<any>>(Component
20
20
  console.error(`🦄 Unistyles: createUnistylesComponent requires ${propName} to be an object. Please check props for component: ${Component.displayName}`)
21
21
  }
22
22
 
23
+ // @ts-expect-error - this is hidden from TS
23
24
  if (props[propName].__unistyles_name && !props[propName].__proto__?.getStyle) {
24
25
  console.error(`🦄 Unistyles: createUnistylesComponent received style that is not bound. You likely used the spread operator on a Unistyle style. Please check props for component: ${Component.displayName}`)
25
26
  }
@@ -34,7 +35,7 @@ export const createUnistylesComponent = <T extends ComponentType<any>>(Component
34
35
 
35
36
  useEffect(() => {
36
37
  const removeChangeListener = (StyleSheet as UnistylesStyleSheet).addChangeListener(dependencies => {
37
- const componentDependencies = (props.style?.__proto__.uni__dependencies || mappings(theme).style?.__proto__.uni__dependencies) as Array<UnistyleDependency>
38
+ const componentDependencies = (props.style?.__proto__.uni__dependencies || mappings?.(theme).style?.__proto__.uni__dependencies) as Array<UnistyleDependency>
38
39
 
39
40
  if (dependencies.includes(UnistyleDependency.Theme) && (!componentDependencies ||componentDependencies.includes(UnistyleDependency.Theme))) {
40
41
  setTheme(UnistylesRuntime.getTheme())
@@ -44,6 +45,7 @@ export const createUnistylesComponent = <T extends ComponentType<any>>(Component
44
45
  if (props?.[propName]) {
45
46
  stylesRef.current = {
46
47
  ...stylesRef.current,
48
+ // @ts-expect-error - this is hidden from TS
47
49
  [propName]: props[propName].__proto__?.getStyle() || props[propName]
48
50
  }
49
51
  }
@@ -62,16 +64,16 @@ export const createUnistylesComponent = <T extends ComponentType<any>>(Component
62
64
  }
63
65
  }, [])
64
66
 
65
- const mergedProps = { ...mappings(theme) } as ComponentProps<T>
67
+ const mergedProps = mappings?.(theme) as Record<string, any> ?? {}
66
68
 
67
69
  Object.keys(props).forEach(key => {
68
70
  if (key in mergedProps) {
69
- mergedProps[key] = Object.assign(props[key], mergedProps[key])
71
+ mergedProps[key] = Object.assign(props[key as keyof typeof props], mergedProps[key])
70
72
 
71
73
  return
72
74
  }
73
75
 
74
- mergedProps[key] = props[key]
76
+ mergedProps[key] = props[key as keyof typeof props]
75
77
  })
76
78
 
77
79
  // override with Unistyles styles
@@ -83,6 +85,6 @@ export const createUnistylesComponent = <T extends ComponentType<any>>(Component
83
85
 
84
86
  isForcedRef.current = false
85
87
 
86
- return <Component {...mergedProps} />
88
+ return <Component {...mergedProps as TProps} />
87
89
  }
88
90
  }
@@ -1,12 +1,78 @@
1
- import React, { type ComponentProps, type ComponentType } from 'react'
1
+ import React, { useEffect, useState, type ComponentType } from 'react'
2
2
  import type { UnistylesTheme } from '../types'
3
-
4
- type Mappings<T extends ComponentType<any>> = (theme: UnistylesTheme) => Partial<Omit<ComponentProps<T>, typeof SUPPORTED_STYLE_PROPS[number]>>
3
+ import { UnistylesRuntime } from '../specs'
4
+ import { UnistyleDependency } from '../specs/NativePlatform'
5
+ import type { PartialBy } from '../types/common'
6
+ import { UnistylesListener } from '../web/listener'
7
+ import { UnistylesShadowRegistry } from '../web'
8
+ import { equal } from '../web/utils'
5
9
 
6
10
  const SUPPORTED_STYLE_PROPS = ['style', 'contentContainerStyle'] as const
11
+ const ALL_DEPENDENCIES = Object.values(UnistyleDependency).filter((dependency): dependency is UnistyleDependency => typeof dependency === 'number')
12
+
13
+ type SupportedStyleProps = typeof SUPPORTED_STYLE_PROPS[number]
14
+
15
+ const useShadowRegistry = (style?: Record<string, any>) => {
16
+ const [classNames, setClassNames] = useState<Array<string>>([])
17
+ const [ref] = useState(document.createElement('div'))
18
+
19
+ if (style) {
20
+ UnistylesShadowRegistry
21
+ .add(ref, style)
22
+ .then(newClassNames => {
23
+ if (equal(classNames, newClassNames)) {
24
+ return
25
+ }
26
+
27
+ setClassNames(newClassNames)
28
+ })
29
+ }
30
+
31
+ useEffect(() => () => {
32
+ // Remove styles on unmount
33
+ UnistylesShadowRegistry.add(null, style)
34
+ })
35
+
36
+ return classNames
37
+ }
38
+
39
+ export const createUnistylesComponent = <TProps extends Record<string, any>, TMappings extends Partial<Omit<TProps, SupportedStyleProps>>>(Component: ComponentType<TProps>, mappings?: (theme: UnistylesTheme) => TMappings) => {
40
+ return (props: PartialBy<TProps, keyof TMappings | SupportedStyleProps>) => {
41
+ const [mappingsProps, setMappingsProps] = useState(mappings?.(UnistylesRuntime.getTheme()))
42
+ const styleClassNames = useShadowRegistry(props.style)
43
+ const contentContainerStyleClassNames = useShadowRegistry(props.contentContainerStyle)
44
+
45
+ useEffect(() => {
46
+ const disposeMappings = UnistylesListener.addListeners(ALL_DEPENDENCIES, () => {
47
+ if (!mappings) {
48
+ disposeMappings()
49
+
50
+ return
51
+ }
52
+
53
+ return setMappingsProps(mappings(UnistylesRuntime.getTheme()))
54
+ })
55
+
56
+ return () => disposeMappings()
57
+ }, [mappingsProps, props.style])
58
+
59
+ const combinedProps = {
60
+ ...mappingsProps,
61
+ ...props,
62
+ ...props.style ? {
63
+ style: {
64
+ $$css: true,
65
+ 'unistyles': styleClassNames.join(' ')
66
+ },
67
+ } : {},
68
+ ...props.contentContainerStyle ? {
69
+ style: {
70
+ $$css: true,
71
+ 'unistyles': contentContainerStyleClassNames.join(' ')
72
+ },
73
+ } : {},
74
+ } as unknown as TProps
7
75
 
8
- export const createUnistylesComponent = <T extends ComponentType<any>>(Component: T, _: Mappings<T> = () => ({})) => {
9
- return (props: ComponentProps<T>) => {
10
- return <Component {...props} />
76
+ return <Component {...combinedProps} />
11
77
  }
12
78
  }
@@ -1,3 +1,4 @@
1
1
  export type Optional<T> = T | undefined
2
2
  export type Nullable<T> = T | null
3
3
  export type SafeReturnType<T> = T extends (...args: any) => infer R ? R : T
4
+ export type PartialBy<T, K> = Omit<T, K & keyof T> & Partial<Pick<T, K & keyof T>>
@@ -2,16 +2,15 @@ import type { UnistylesValues } from '../../types'
2
2
  import { isPseudo } from './pseudo'
3
3
  import { getStyle } from './style'
4
4
  import { deepMergeObjects } from '../utils'
5
- import { getTransformStyle } from './transform'
6
- import { isBoxShadow, isTextShadow, isTransform } from './utils'
7
- import { getTextShadowStyle } from './textShadow'
8
- import { getBoxShadowStyle } from './boxShadow'
5
+ import { getTransformStyle, getBoxShadow, getFilterStyle } from './object'
6
+ import { isShadow, isFilter, isTextShadow, isTransform, isBoxShadow } from './utils'
7
+ import { getTextShadowStyle, getBoxShadowStyle } from './shadow'
9
8
 
10
9
  export const convertUnistyles = (value: UnistylesValues) => {
11
10
  // Flag to mark if textShadow is already created
12
11
  let hasTextShadow = false
13
12
  // Flag to mark if boxShadow is already created
14
- let hasBoxShadow = false
13
+ let hasShadow = false
15
14
 
16
15
  const stylesArray = Object.entries({
17
16
  ...value,
@@ -40,17 +39,25 @@ export const convertUnistyles = (value: UnistylesValues) => {
40
39
  return getTextShadowStyle(value)
41
40
  }
42
41
 
43
- // Box shadow
44
- if (isBoxShadow(unistylesKey)) {
45
- if (hasBoxShadow) {
42
+ // RN shadows
43
+ if (isShadow(unistylesKey)) {
44
+ if (hasShadow) {
46
45
  return []
47
46
  }
48
47
 
49
- hasBoxShadow = true
48
+ hasShadow = true
50
49
 
51
50
  return getBoxShadowStyle(value)
52
51
  }
53
52
 
53
+ if (isFilter(unistylesKey, unistylesValue)) {
54
+ return getFilterStyle(unistylesValue)
55
+ }
56
+
57
+ if (isBoxShadow(unistylesKey, unistylesValue)) {
58
+ return getBoxShadow(unistylesValue)
59
+ }
60
+
54
61
  // Transforms
55
62
  if (isTransform(unistylesKey, unistylesValue)) {
56
63
  return getTransformStyle(unistylesValue)
@@ -0,0 +1,54 @@
1
+ import type { BoxShadowValue } from 'react-native'
2
+ import { normalizeNumericValue } from '../utils'
3
+ import { deepMergeObjects, keyInObject } from '../../utils'
4
+
5
+ const createBoxShadowValue = (style: BoxShadowValue) => {
6
+ const { offsetX, offsetY, blurRadius = 0, spreadDistance = 0, color = '#000', inset } = style
7
+
8
+ return `${inset ? 'inset ' : ''}${normalizeNumericValue(offsetX)} ${normalizeNumericValue(offsetY)} ${normalizeNumericValue(blurRadius as number)} ${normalizeNumericValue(spreadDistance as number)} ${color}`
9
+ }
10
+
11
+ export const getBoxShadow = (boxShadow: Array<BoxShadowValue>) => {
12
+ const breakpoints = new Set<string>()
13
+ boxShadow.forEach(shadow => {
14
+ const [key] = Object.keys(shadow)
15
+ const value = shadow[key as keyof BoxShadowValue]
16
+
17
+ // Breakpoints
18
+ if (typeof value === 'object') {
19
+ Object.keys(value).forEach(breakpoint => breakpoints.add(breakpoint))
20
+ }
21
+ })
22
+
23
+ if (breakpoints.size === 0) {
24
+ const boxShadowStyle = Object.fromEntries(boxShadow.map(shadow => {
25
+ const [key] = Object.keys(shadow)
26
+ return [key, shadow[key as keyof BoxShadowValue]]
27
+ }))
28
+
29
+ return {
30
+ boxShadow: createBoxShadowValue(boxShadowStyle)
31
+ }
32
+ }
33
+
34
+ const breakpointStyles = Array.from(breakpoints).map(breakpoint => {
35
+ const styles = Object.fromEntries(boxShadow.map(shadow => {
36
+ const [key] = Object.keys(shadow)
37
+ const value = shadow[key as keyof BoxShadowValue]
38
+
39
+ if (typeof value === 'object' && keyInObject(value, breakpoint)) {
40
+ return [key, value[breakpoint]]
41
+ }
42
+
43
+ return [key, value]
44
+ }))
45
+
46
+ return {
47
+ [breakpoint]: {
48
+ boxShadow: createBoxShadowValue(styles)
49
+ }
50
+ }
51
+ })
52
+
53
+ return deepMergeObjects(...breakpointStyles)
54
+ }
@@ -0,0 +1,39 @@
1
+ import type { DropShadowValue } from 'react-native'
2
+ import { hyphenate } from '../../utils'
3
+ import type { Filters } from '../types'
4
+ import { getObjectStyle } from './objectStyle'
5
+ import { normalizeColor, normalizeNumericValue } from '../utils'
6
+ import { UnistylesRuntime } from '../../runtime'
7
+ import { isUnistylesMq } from '../../../mq'
8
+
9
+ const getDropShadowStyle = (dropShadow: DropShadowValue) => {
10
+ const { offsetX = 0, offsetY = 0, standardDeviation = 0, color = '#000' } = dropShadow
11
+
12
+ return `${normalizeColor(String(color))} ${normalizeNumericValue(offsetX)} ${normalizeNumericValue(offsetY)} ${normalizeNumericValue(standardDeviation)}`
13
+ }
14
+
15
+ export const getFilterStyle = (filters: Array<Filters>) => {
16
+ const restFilters = filters.filter(filter => Object.keys(filter)[0] !== 'dropShadow')
17
+ const dropShadow = (() => {
18
+ const dropShadowValue = filters.find(filter => Object.keys(filter)[0] === 'dropShadow')?.dropShadow as Record<string, any>
19
+
20
+ if (typeof dropShadowValue !== 'object') {
21
+ return []
22
+ }
23
+
24
+ const breakpoints = Object.keys(dropShadowValue).filter(key => Object.keys(UnistylesRuntime.breakpoints).includes(key) || isUnistylesMq(key))
25
+ const breakpointsDropShadow = Object.fromEntries(breakpoints.map(breakpoint => [breakpoint, getDropShadowStyle(dropShadowValue[breakpoint])]))
26
+
27
+ if (breakpoints.length === 0) {
28
+ return [{
29
+ dropShadow: getDropShadowStyle(dropShadowValue as DropShadowValue)
30
+ }]
31
+ }
32
+
33
+ return [{
34
+ dropShadow: breakpointsDropShadow
35
+ }]
36
+ })()
37
+
38
+ return getObjectStyle([...restFilters, ...dropShadow], 'filter', (key, value) => `${hyphenate(key)}(${normalizeNumericValue(value as number | string)})`)
39
+ }
@@ -0,0 +1,3 @@
1
+ export * from './transform'
2
+ export * from './boxShadow'
3
+ export * from './filter'
@@ -0,0 +1,68 @@
1
+ import { deepMergeObjects, keyInObject } from '../../utils'
2
+
3
+ type Styles = Record<string, any>
4
+ type Normalize<TStyles extends Styles> = (key: keyof TStyles, value: TStyles[keyof TStyles]) => any
5
+
6
+ const createStylesValue = <TStyles extends Styles>(styles: Array<TStyles>, normalize: Normalize<TStyles>) => styles
7
+ .map(style => {
8
+ const [key] = Object.keys(style)
9
+
10
+ if (!key) {
11
+ return undefined
12
+ }
13
+
14
+ return normalize(key, style[key])
15
+ })
16
+ .filter(Boolean)
17
+ .join(' ')
18
+
19
+ export const getObjectStyle = <TStyles extends Styles>(styles: Array<TStyles>, styleKey: string, normalize: Normalize<TStyles>) => {
20
+ const breakpoints = new Set<string>()
21
+ const normalStyles: Array<TStyles> = []
22
+
23
+ styles.forEach(style => {
24
+ const [property] = Object.keys(style)
25
+
26
+ if (!property) {
27
+ return
28
+ }
29
+
30
+ const value = style[property]
31
+
32
+ if (typeof value === 'object' && !Array.isArray(value)) {
33
+ Object.keys(value ?? {}).forEach(breakpoint => breakpoints.add(breakpoint))
34
+
35
+ return
36
+ }
37
+
38
+ normalStyles.push(style)
39
+ })
40
+
41
+ const breakpointStyles = Array.from(breakpoints).flatMap(breakpoint => {
42
+ const stylesPerBreakpoint = styles.flatMap(style => {
43
+ const [property] = Object.keys(style)
44
+
45
+ if (!property) {
46
+ return []
47
+ }
48
+
49
+ const value = style[property]
50
+
51
+ if (typeof value === 'object' && !Array.isArray(value)) {
52
+ return keyInObject(value, breakpoint) ? [{ [property]: value[breakpoint] }] : []
53
+ }
54
+
55
+ return []
56
+ }) as Array<TStyles>
57
+
58
+ return [{
59
+ [breakpoint]: {
60
+ [styleKey]: createStylesValue(stylesPerBreakpoint, normalize)
61
+ }
62
+ }]
63
+ })
64
+
65
+ return deepMergeObjects<Record<string, any>>({
66
+ [styleKey]: createStylesValue(normalStyles, normalize)
67
+ }, ...breakpointStyles)
68
+ }
@@ -0,0 +1,24 @@
1
+ import { getObjectStyle } from './objectStyle'
2
+ import { normalizeNumericValue } from '../utils'
3
+ import type { TransformStyles } from '../../../types/core'
4
+
5
+ const normalizeTransform = (key: string, value: any) => {
6
+ if (key.includes('scale')) {
7
+ return value
8
+ }
9
+
10
+ if (typeof value === 'number') {
11
+ return normalizeNumericValue(value)
12
+ }
13
+
14
+ return value
15
+ }
16
+
17
+ export const getTransformStyle = (transforms: Array<TransformStyles>) => getObjectStyle(transforms, 'transform', (key, value) => {
18
+ switch(key) {
19
+ case 'matrix':
20
+ return `${key}(${(value as Array<number>).join(',')})`
21
+ default:
22
+ return `${key}(${normalizeTransform(key, value)})`
23
+ }
24
+ })
@@ -1,41 +1,20 @@
1
- import { deepMergeObjects, warn } from '../utils'
2
- import { validateShadow } from './shadow'
3
- import { BOX_SHADOW_STYLES, type BoxShadow } from './types'
4
- import { extractShadowValue, normalizeColor, normalizeNumericValue } from './utils'
1
+ import { deepMergeObjects } from '../../utils'
2
+ import { BOX_SHADOW_STYLES, type BoxShadow } from '../types'
3
+ import { extractShadowValue, normalizeColor, normalizeNumericValue } from '../utils'
4
+ import { getShadowBreakpoints } from './getShadowBreakpoints'
5
5
 
6
6
  const createBoxShadowValue = (style: BoxShadow) => {
7
- // at this point every prop is present
8
7
  const { shadowColor, shadowOffset, shadowOpacity, shadowRadius } = style
9
- const offsetX = normalizeNumericValue(shadowOffset.width)
10
- const offsetY = normalizeNumericValue(shadowOffset.height)
11
- const radius = normalizeNumericValue(shadowRadius)
12
- const color = normalizeColor(shadowColor as string, shadowOpacity as number)
8
+ const offsetX = normalizeNumericValue(shadowOffset?.width ?? 0)
9
+ const offsetY = normalizeNumericValue(shadowOffset?.height ?? 0)
10
+ const radius = normalizeNumericValue(shadowRadius ?? 0)
11
+ const color = normalizeColor((shadowColor ?? '#000000') as string, (shadowOpacity ?? 1) as number)
13
12
 
14
13
  return `${offsetX} ${offsetY} ${radius} ${color}`
15
14
  }
16
15
 
17
16
  export const getBoxShadowStyle = (styles: Record<string, any>) => {
18
- const missingStyles = BOX_SHADOW_STYLES.filter(key => !(key in styles))
19
-
20
- if (missingStyles.length) {
21
- warn(`can't apply box shadow as you miss these properties: ${missingStyles.join(', ')}`)
22
-
23
- return {}
24
- }
25
-
26
- const breakpointsSet = new Set<string>()
27
-
28
- try {
29
- validateShadow(BOX_SHADOW_STYLES, styles, breakpointsSet)
30
- } catch (error) {
31
- if (typeof error === 'string') {
32
- warn(error)
33
- }
34
-
35
- return {}
36
- }
37
-
38
- const breakpoints = Array.from(breakpointsSet)
17
+ const breakpoints = getShadowBreakpoints(BOX_SHADOW_STYLES, styles)
39
18
 
40
19
  // If no breakpoints were used return styles without media queries
41
20
  if (breakpoints.length === 0) {
@@ -0,0 +1,34 @@
1
+ import type { ShadowOffset } from '../types'
2
+
3
+ export const getShadowBreakpoints = (shadowProperties: ReadonlyArray<string>, styles: Record<string, any>) => {
4
+ const breakpoints = new Set<string>()
5
+
6
+ shadowProperties.forEach(key => {
7
+ const value = styles[key]
8
+
9
+ if (typeof value !== 'object') {
10
+ return
11
+ }
12
+
13
+ if (key === 'shadowOffset' || key === 'textShadowOffset') {
14
+ const { width, height } = value as ShadowOffset
15
+
16
+ // If shadowOffset.width has breakpoints
17
+ if (typeof width === 'object') {
18
+ Object.keys(width).forEach(breakpoint => breakpoints.add(breakpoint))
19
+ }
20
+
21
+ // If shadowOffset.height has breakpoints
22
+ if (typeof height === 'object') {
23
+ Object.keys(height).forEach(breakpoint => breakpoints.add(breakpoint))
24
+ }
25
+
26
+ return
27
+ }
28
+
29
+ // Collect regular breakpoints
30
+ Object.keys(value).forEach(breakpoint => breakpoints.add(breakpoint))
31
+ })
32
+
33
+ return Array.from(breakpoints)
34
+ }
@@ -0,0 +1,2 @@
1
+ export * from './boxShadow'
2
+ export * from './textShadow'
@@ -1,40 +1,20 @@
1
- import { deepMergeObjects, warn } from '../utils'
2
- import { validateShadow } from './shadow'
3
- import { TEXT_SHADOW_STYLES, type TextShadow } from './types'
4
- import { extractShadowValue, normalizeColor, normalizeNumericValue } from './utils'
1
+ import { deepMergeObjects } from '../../utils'
2
+ import { TEXT_SHADOW_STYLES, type TextShadow } from '../types'
3
+ import { extractShadowValue, normalizeColor, normalizeNumericValue } from '../utils'
4
+ import { getShadowBreakpoints } from './getShadowBreakpoints'
5
5
 
6
6
  const createTextShadowValue = (style: TextShadow) => {
7
7
  const { textShadowColor, textShadowOffset, textShadowRadius } = style
8
- const offsetX = normalizeNumericValue(textShadowOffset.width)
9
- const offsetY = normalizeNumericValue(textShadowOffset.height)
10
- const radius = normalizeNumericValue(textShadowRadius)
11
- const color = normalizeColor(textShadowColor as string)
8
+ const offsetX = normalizeNumericValue(textShadowOffset?.width ?? 0)
9
+ const offsetY = normalizeNumericValue(textShadowOffset?.height ?? 0)
10
+ const radius = normalizeNumericValue(textShadowRadius ?? 0)
11
+ const color = normalizeColor((textShadowColor ?? '#000000') as string)
12
12
 
13
13
  return `${offsetX} ${offsetY} ${radius} ${color}`
14
14
  }
15
15
 
16
16
  export const getTextShadowStyle = (styles: Record<string, any>) => {
17
- const missingStyles = TEXT_SHADOW_STYLES.filter(key => !(key in styles))
18
-
19
- if (missingStyles.length) {
20
- warn(`can't apply text shadow as you miss these properties: ${missingStyles.join(', ')}`)
21
-
22
- return {}
23
- }
24
-
25
- const breakpointsSet = new Set<string>()
26
-
27
- try {
28
- validateShadow(TEXT_SHADOW_STYLES, styles, breakpointsSet)
29
- } catch (error) {
30
- if (typeof error === 'string') {
31
- warn(error)
32
- }
33
-
34
- return {}
35
- }
36
-
37
- const breakpoints = Array.from(breakpointsSet)
17
+ const breakpoints = getShadowBreakpoints(TEXT_SHADOW_STYLES, styles)
38
18
 
39
19
  // If no breakpoints were used return styles without media queries
40
20
  if (breakpoints.length === 0) {
@@ -1,4 +1,4 @@
1
- import type { TextStyle, ViewStyle } from 'react-native'
1
+ import type { FilterFunction, TextStyle, ViewStyle } from 'react-native'
2
2
  import type { ToDeepUnistyles } from '../../types/stylesheet'
3
3
 
4
4
  export type ShadowOffset = ToDeepUnistyles<{ width: number, height: number }>
@@ -14,3 +14,10 @@ export type BoxShadow = Required<Pick<ViewStyle, typeof BOX_SHADOW_STYLES[number
14
14
  export type AllShadow = TextShadow & BoxShadow
15
15
 
16
16
  export type AllShadowKeys = keyof AllShadow
17
+
18
+ type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never
19
+ type FilterKeys = keyof UnionToIntersection<FilterFunction>
20
+
21
+ export type Filters = {
22
+ [K in FilterKeys]: UnionToIntersection<FilterFunction>[K]
23
+ }