@expo/ui 56.0.6 → 56.0.8

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 (110) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/java/expo/modules/ui/ExpoUIModule.kt +6 -2
  4. package/android/src/main/java/expo/modules/ui/HostView.kt +2 -0
  5. package/android/src/main/java/expo/modules/ui/ModifierRegistry.kt +19 -1
  6. package/android/src/main/java/expo/modules/ui/RNHostView.kt +8 -3
  7. package/android/src/main/java/expo/modules/ui/ShadowNodeSyncFlush.kt +28 -0
  8. package/build/community/menu/MenuView.android.d.ts +16 -0
  9. package/build/community/menu/MenuView.android.d.ts.map +1 -0
  10. package/build/community/menu/MenuView.d.ts +19 -0
  11. package/build/community/menu/MenuView.d.ts.map +1 -0
  12. package/build/community/menu/MenuView.ios.d.ts +10 -0
  13. package/build/community/menu/MenuView.ios.d.ts.map +1 -0
  14. package/build/community/menu/index.d.ts +5 -0
  15. package/build/community/menu/index.d.ts.map +1 -0
  16. package/build/community/menu/types.d.ts +166 -0
  17. package/build/community/menu/types.d.ts.map +1 -0
  18. package/build/jetpack-compose/modifiers/index.d.ts +15 -0
  19. package/build/jetpack-compose/modifiers/index.d.ts.map +1 -1
  20. package/build/swift-ui/Alert/index.d.ts +42 -0
  21. package/build/swift-ui/Alert/index.d.ts.map +1 -0
  22. package/build/swift-ui/SlotView.d.ts +5 -2
  23. package/build/swift-ui/SlotView.d.ts.map +1 -1
  24. package/build/swift-ui/SwipeActions/index.d.ts +38 -0
  25. package/build/swift-ui/SwipeActions/index.d.ts.map +1 -0
  26. package/build/swift-ui/TabView/index.d.ts +1 -1
  27. package/build/swift-ui/TabView/index.d.ts.map +1 -1
  28. package/build/swift-ui/index.d.ts +2 -0
  29. package/build/swift-ui/index.d.ts.map +1 -1
  30. package/build/swift-ui/modifiers/index.d.ts +3 -1
  31. package/build/swift-ui/modifiers/index.d.ts.map +1 -1
  32. package/build/swift-ui/modifiers/symbolEffect.d.ts +103 -0
  33. package/build/swift-ui/modifiers/symbolEffect.d.ts.map +1 -0
  34. package/build/universal/Column/index.android.d.ts.map +1 -1
  35. package/build/universal/Host/index.d.ts +17 -6
  36. package/build/universal/Host/index.d.ts.map +1 -1
  37. package/build/universal/Row/index.android.d.ts.map +1 -1
  38. package/build/universal/TextInput/types.d.ts +1 -1
  39. package/build/universal/TextInput/types.d.ts.map +1 -1
  40. package/expo-module.config.json +1 -1
  41. package/ios/Alert/Alert.swift +56 -0
  42. package/ios/Alert/AlertProps.swift +8 -0
  43. package/ios/ExpoUIModule.swift +2 -0
  44. package/ios/ExpoUITouchHandlerHelper.h +4 -1
  45. package/ios/ExpoUITouchHandlerHelper.mm +1 -0
  46. package/ios/Modifiers/SwipeActionsModifier.swift +97 -0
  47. package/ios/Modifiers/SymbolEffectModifier.swift +452 -0
  48. package/ios/Modifiers/ViewModifierRegistry.swift +4 -0
  49. package/ios/SlotView.swift +5 -0
  50. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.6/expo.modules.ui-56.0.6-sources.jar → 56.0.8/expo.modules.ui-56.0.8-sources.jar} +0 -0
  51. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8-sources.jar.md5 +1 -0
  52. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8-sources.jar.sha1 +1 -0
  53. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8-sources.jar.sha256 +1 -0
  54. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8-sources.jar.sha512 +1 -0
  55. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.aar +0 -0
  56. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.aar.md5 +1 -0
  57. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.aar.sha1 +1 -0
  58. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.aar.sha256 +1 -0
  59. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.aar.sha512 +1 -0
  60. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.6/expo.modules.ui-56.0.6.module → 56.0.8/expo.modules.ui-56.0.8.module} +22 -22
  61. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.module.md5 +1 -0
  62. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.module.sha1 +1 -0
  63. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.module.sha256 +1 -0
  64. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.module.sha512 +1 -0
  65. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.6/expo.modules.ui-56.0.6.pom → 56.0.8/expo.modules.ui-56.0.8.pom} +1 -1
  66. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.pom.md5 +1 -0
  67. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.pom.sha1 +1 -0
  68. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.pom.sha256 +1 -0
  69. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.pom.sha512 +1 -0
  70. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml +4 -4
  71. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.md5 +1 -1
  72. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha1 +1 -1
  73. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha256 +1 -1
  74. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha512 +1 -1
  75. package/package.json +7 -3
  76. package/src/community/menu/MenuView.android.tsx +224 -0
  77. package/src/community/menu/MenuView.ios.tsx +149 -0
  78. package/src/community/menu/MenuView.tsx +36 -0
  79. package/src/community/menu/index.tsx +14 -0
  80. package/src/community/menu/types.tsx +171 -0
  81. package/src/jetpack-compose/modifiers/index.ts +25 -0
  82. package/src/swift-ui/Alert/index.tsx +87 -0
  83. package/src/swift-ui/SlotView.tsx +17 -4
  84. package/src/swift-ui/SwipeActions/index.tsx +73 -0
  85. package/src/swift-ui/TabView/index.tsx +1 -1
  86. package/src/swift-ui/index.tsx +2 -0
  87. package/src/swift-ui/modifiers/index.ts +3 -0
  88. package/src/swift-ui/modifiers/symbolEffect.ts +181 -0
  89. package/src/ts-declarations/react-native-web.d.ts +20 -0
  90. package/src/universal/Column/index.android.tsx +1 -1
  91. package/src/universal/Host/index.tsx +70 -5
  92. package/src/universal/Row/index.android.tsx +1 -1
  93. package/src/universal/TextInput/types.ts +1 -1
  94. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.6/expo.modules.ui-56.0.6-sources.jar.md5 +0 -1
  95. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.6/expo.modules.ui-56.0.6-sources.jar.sha1 +0 -1
  96. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.6/expo.modules.ui-56.0.6-sources.jar.sha256 +0 -1
  97. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.6/expo.modules.ui-56.0.6-sources.jar.sha512 +0 -1
  98. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.6/expo.modules.ui-56.0.6.aar +0 -0
  99. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.6/expo.modules.ui-56.0.6.aar.md5 +0 -1
  100. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.6/expo.modules.ui-56.0.6.aar.sha1 +0 -1
  101. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.6/expo.modules.ui-56.0.6.aar.sha256 +0 -1
  102. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.6/expo.modules.ui-56.0.6.aar.sha512 +0 -1
  103. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.6/expo.modules.ui-56.0.6.module.md5 +0 -1
  104. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.6/expo.modules.ui-56.0.6.module.sha1 +0 -1
  105. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.6/expo.modules.ui-56.0.6.module.sha256 +0 -1
  106. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.6/expo.modules.ui-56.0.6.module.sha512 +0 -1
  107. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.6/expo.modules.ui-56.0.6.pom.md5 +0 -1
  108. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.6/expo.modules.ui-56.0.6.pom.sha1 +0 -1
  109. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.6/expo.modules.ui-56.0.6.pom.sha256 +0 -1
  110. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.6/expo.modules.ui-56.0.6.pom.sha512 +0 -1
@@ -0,0 +1,452 @@
1
+ // Copyright 2026-present 650 Industries. All rights reserved.
2
+
3
+ import ExpoModulesCore
4
+ import SwiftUI
5
+
6
+ // MARK: - Effect kind
7
+
8
+ internal enum SymbolEffectKind: String, Enumerable {
9
+ case appear
10
+ case bounce
11
+ case breathe
12
+ case disappear
13
+ case drawOff
14
+ case drawOn
15
+ case pulse
16
+ case rotate
17
+ case scale
18
+ case variableColor
19
+ case wiggle
20
+ }
21
+
22
+ // MARK: - Effect option enums
23
+
24
+ internal enum SymbolEffectScope: String, Enumerable {
25
+ case byLayer
26
+ case wholeSymbol
27
+ case individually
28
+ }
29
+
30
+ internal enum SymbolEffectDirection: String, Enumerable {
31
+ case up
32
+ case down
33
+ case left
34
+ case right
35
+ case forward
36
+ case backward
37
+ case clockwise
38
+ case counterClockwise
39
+ }
40
+
41
+ internal enum SymbolEffectScale: String, Enumerable {
42
+ case up
43
+ case down
44
+ }
45
+
46
+ internal enum SymbolEffectBreatheStyle: String, Enumerable {
47
+ case plain
48
+ case pulse
49
+ }
50
+
51
+ internal enum SymbolEffectFillStyle: String, Enumerable {
52
+ case iterative
53
+ case cumulative
54
+ }
55
+
56
+ internal enum SymbolEffectPlaybackStyle: String, Enumerable {
57
+ case reversing
58
+ case nonReversing
59
+ case reversed
60
+ case nonReversed
61
+ }
62
+
63
+ internal enum SymbolEffectInactiveLayers: String, Enumerable {
64
+ case dim
65
+ case hide
66
+ }
67
+
68
+ // MARK: - Options config
69
+
70
+ internal enum SymbolEffectRepeatKind: String, Enumerable {
71
+ case continuous
72
+ case nonRepeating
73
+ case periodic
74
+ }
75
+
76
+ internal struct SymbolEffectOptionsConfig: Record {
77
+ @Field var repeatKind: SymbolEffectRepeatKind?
78
+ @Field var repeatCount: Int?
79
+ @Field var repeatDelay: Double?
80
+ @Field var speed: Double?
81
+
82
+ @available(iOS 17.0, tvOS 17.0, *)
83
+ func toSwiftUI() -> SymbolEffectOptions {
84
+ var options: SymbolEffectOptions = .default
85
+
86
+ switch repeatKind {
87
+ case .nonRepeating:
88
+ options = .nonRepeating
89
+ case .continuous:
90
+ if #available(iOS 18.0, tvOS 18.0, *) {
91
+ options = .repeat(.continuous)
92
+ }
93
+ // iOS 17: indefinite effects loop by default, so `.default` is fine here.
94
+ case .periodic:
95
+ if #available(iOS 18.0, tvOS 18.0, *) {
96
+ options = .repeat(.periodic(repeatCount, delay: repeatDelay))
97
+ }
98
+ case .none:
99
+ break
100
+ }
101
+
102
+ if let speed {
103
+ options = options.speed(speed)
104
+ }
105
+ return options
106
+ }
107
+ }
108
+
109
+ // MARK: - Effect config
110
+
111
+ internal struct SymbolEffectConfig: Record {
112
+ @Field var effect: SymbolEffectKind = .pulse
113
+ @Field var direction: SymbolEffectDirection?
114
+ @Field var scale: SymbolEffectScale?
115
+ @Field var style: SymbolEffectBreatheStyle?
116
+ @Field var customAngle: Double?
117
+ @Field var fillStyle: SymbolEffectFillStyle?
118
+ @Field var playbackStyle: SymbolEffectPlaybackStyle?
119
+ @Field var inactiveLayers: SymbolEffectInactiveLayers?
120
+ @Field var scope: SymbolEffectScope?
121
+ }
122
+
123
+ // MARK: - Modifier
124
+
125
+ internal struct SymbolEffectModifier: ViewModifier, Record {
126
+ @Field var effect: SymbolEffectConfig
127
+ @Field var options: SymbolEffectOptionsConfig?
128
+ @Field var isActive: ObservableState?
129
+ @Field var value: ObservableState?
130
+
131
+ @ViewBuilder
132
+ func body(content: Content) -> some View {
133
+ if #available(iOS 17.0, tvOS 17.0, *) {
134
+ let resolvedOptions = options?.toSwiftUI() ?? .default
135
+ if let value {
136
+ DiscreteEffectView(
137
+ config: effect,
138
+ options: resolvedOptions,
139
+ state: value
140
+ ) {
141
+ content
142
+ }
143
+ } else if let isActive {
144
+ IndefiniteEffectView(
145
+ config: effect,
146
+ options: resolvedOptions,
147
+ state: isActive
148
+ ) {
149
+ content
150
+ }
151
+ } else {
152
+ applyIndefiniteEffect(
153
+ to: content,
154
+ config: effect,
155
+ options: resolvedOptions,
156
+ isActive: true
157
+ )
158
+ }
159
+ } else {
160
+ content
161
+ }
162
+ }
163
+ }
164
+
165
+ // MARK: - Per-effect builders
166
+
167
+ @available(iOS 17.0, tvOS 17.0, *)
168
+ private func buildPulseEffect(_ config: SymbolEffectConfig) -> PulseSymbolEffect {
169
+ return switch config.scope {
170
+ case .byLayer: .pulse.byLayer
171
+ case .wholeSymbol: .pulse.wholeSymbol
172
+ default: .pulse
173
+ }
174
+ }
175
+
176
+ @available(iOS 17.0, tvOS 17.0, *)
177
+ private func buildBounceEffect(_ config: SymbolEffectConfig) -> BounceSymbolEffect {
178
+ let directed: BounceSymbolEffect = switch config.direction {
179
+ case .up: .bounce.up
180
+ case .down: .bounce.down
181
+ default: .bounce
182
+ }
183
+ return switch config.scope {
184
+ case .byLayer: directed.byLayer
185
+ case .wholeSymbol: directed.wholeSymbol
186
+ default: directed
187
+ }
188
+ }
189
+
190
+ @available(iOS 17.0, tvOS 17.0, *)
191
+ private func buildVariableColorEffect(_ config: SymbolEffectConfig) -> VariableColorSymbolEffect {
192
+ let filled: VariableColorSymbolEffect = switch config.fillStyle {
193
+ case .iterative: .variableColor.iterative
194
+ case .cumulative: .variableColor.cumulative
195
+ default: .variableColor
196
+ }
197
+ let playing: VariableColorSymbolEffect = switch config.playbackStyle {
198
+ case .reversing: filled.reversing
199
+ case .nonReversing: filled.nonReversing
200
+ default: filled
201
+ }
202
+ return switch config.inactiveLayers {
203
+ case .dim: playing.dimInactiveLayers
204
+ case .hide: playing.hideInactiveLayers
205
+ default: playing
206
+ }
207
+ }
208
+
209
+ @available(iOS 17.0, tvOS 17.0, *)
210
+ private func buildScaleEffect(_ config: SymbolEffectConfig) -> ScaleSymbolEffect {
211
+ let scaled: ScaleSymbolEffect = switch config.scale {
212
+ case .up: .scale.up
213
+ case .down: .scale.down
214
+ default: .scale
215
+ }
216
+ return switch config.scope {
217
+ case .byLayer: scaled.byLayer
218
+ case .wholeSymbol: scaled.wholeSymbol
219
+ default: scaled
220
+ }
221
+ }
222
+
223
+ @available(iOS 17.0, tvOS 17.0, *)
224
+ private func buildAppearEffect(_ config: SymbolEffectConfig) -> AppearSymbolEffect {
225
+ let scaled: AppearSymbolEffect = switch config.scale {
226
+ case .up: .appear.up
227
+ case .down: .appear.down
228
+ default: .appear
229
+ }
230
+ return switch config.scope {
231
+ case .byLayer: scaled.byLayer
232
+ case .wholeSymbol: scaled.wholeSymbol
233
+ default: scaled
234
+ }
235
+ }
236
+
237
+ @available(iOS 17.0, tvOS 17.0, *)
238
+ private func buildDisappearEffect(_ config: SymbolEffectConfig) -> DisappearSymbolEffect {
239
+ let scaled: DisappearSymbolEffect = switch config.scale {
240
+ case .up: .disappear.up
241
+ case .down: .disappear.down
242
+ default: .disappear
243
+ }
244
+ return switch config.scope {
245
+ case .byLayer: scaled.byLayer
246
+ case .wholeSymbol: scaled.wholeSymbol
247
+ default: scaled
248
+ }
249
+ }
250
+
251
+ @available(iOS 18.0, tvOS 18.0, *)
252
+ private func buildWiggleEffect(_ config: SymbolEffectConfig) -> WiggleSymbolEffect {
253
+ let directed: WiggleSymbolEffect = if let angle = config.customAngle {
254
+ .wiggle.custom(angle: angle)
255
+ } else {
256
+ switch config.direction {
257
+ case .up: .wiggle.up
258
+ case .down: .wiggle.down
259
+ case .left: .wiggle.left
260
+ case .right: .wiggle.right
261
+ case .forward: .wiggle.forward
262
+ case .backward: .wiggle.backward
263
+ case .clockwise: .wiggle.clockwise
264
+ case .counterClockwise: .wiggle.counterClockwise
265
+ default: .wiggle
266
+ }
267
+ }
268
+ return switch config.scope {
269
+ case .byLayer: directed.byLayer
270
+ case .wholeSymbol: directed.wholeSymbol
271
+ default: directed
272
+ }
273
+ }
274
+
275
+ @available(iOS 18.0, tvOS 18.0, *)
276
+ private func buildBreatheEffect(_ config: SymbolEffectConfig) -> BreatheSymbolEffect {
277
+ let styled: BreatheSymbolEffect = switch config.style {
278
+ case .pulse: .breathe.pulse
279
+ case .plain: .breathe.plain
280
+ default: .breathe
281
+ }
282
+ return switch config.scope {
283
+ case .byLayer: styled.byLayer
284
+ case .wholeSymbol: styled.wholeSymbol
285
+ default: styled
286
+ }
287
+ }
288
+
289
+ @available(iOS 18.0, tvOS 18.0, *)
290
+ private func buildRotateEffect(_ config: SymbolEffectConfig) -> RotateSymbolEffect {
291
+ let directed: RotateSymbolEffect = switch config.direction {
292
+ case .clockwise: .rotate.clockwise
293
+ case .counterClockwise: .rotate.counterClockwise
294
+ default: .rotate
295
+ }
296
+ return switch config.scope {
297
+ case .byLayer: directed.byLayer
298
+ case .wholeSymbol: directed.wholeSymbol
299
+ default: directed
300
+ }
301
+ }
302
+
303
+ @available(iOS 26.0, tvOS 26.0, *)
304
+ private func buildDrawOnEffect(_ config: SymbolEffectConfig) -> DrawOnSymbolEffect {
305
+ return switch config.scope {
306
+ case .byLayer: .drawOn.byLayer
307
+ case .individually: .drawOn.individually
308
+ case .wholeSymbol: .drawOn.wholeSymbol
309
+ default: .drawOn
310
+ }
311
+ }
312
+
313
+ @available(iOS 26.0, tvOS 26.0, *)
314
+ private func buildDrawOffEffect(_ config: SymbolEffectConfig) -> DrawOffSymbolEffect {
315
+ let played: DrawOffSymbolEffect = switch config.playbackStyle {
316
+ case .reversed: .drawOff.reversed
317
+ case .nonReversed: .drawOff.nonReversed
318
+ default: .drawOff
319
+ }
320
+ return switch config.scope {
321
+ case .byLayer: played.byLayer
322
+ case .individually: played.individually
323
+ case .wholeSymbol: played.wholeSymbol
324
+ default: played
325
+ }
326
+ }
327
+
328
+ // MARK: - Dispatch
329
+
330
+ @available(iOS 17.0, tvOS 17.0, *)
331
+ @ViewBuilder
332
+ private func applyIndefiniteEffect<TargetView: View>(
333
+ to view: TargetView,
334
+ config: SymbolEffectConfig,
335
+ options: SymbolEffectOptions,
336
+ isActive: Bool
337
+ ) -> some View {
338
+ switch config.effect {
339
+ case .pulse:
340
+ view.symbolEffect(buildPulseEffect(config), options: options, isActive: isActive)
341
+ case .bounce:
342
+ view.symbolEffect(buildBounceEffect(config), options: options, isActive: isActive)
343
+ case .variableColor:
344
+ view.symbolEffect(buildVariableColorEffect(config), options: options, isActive: isActive)
345
+ case .scale:
346
+ view.symbolEffect(buildScaleEffect(config), options: options, isActive: isActive)
347
+ case .appear:
348
+ view.symbolEffect(buildAppearEffect(config), options: options, isActive: isActive)
349
+ case .disappear:
350
+ view.symbolEffect(buildDisappearEffect(config), options: options, isActive: isActive)
351
+ case .wiggle:
352
+ if #available(iOS 18.0, tvOS 18.0, *) {
353
+ view.symbolEffect(buildWiggleEffect(config), options: options, isActive: isActive)
354
+ } else {
355
+ view
356
+ }
357
+ case .breathe:
358
+ if #available(iOS 18.0, tvOS 18.0, *) {
359
+ view.symbolEffect(buildBreatheEffect(config), options: options, isActive: isActive)
360
+ } else {
361
+ view
362
+ }
363
+ case .rotate:
364
+ if #available(iOS 18.0, tvOS 18.0, *) {
365
+ view.symbolEffect(buildRotateEffect(config), options: options, isActive: isActive)
366
+ } else {
367
+ view
368
+ }
369
+ case .drawOn:
370
+ if #available(iOS 26.0, tvOS 26.0, *) {
371
+ view.symbolEffect(buildDrawOnEffect(config), options: options, isActive: isActive)
372
+ } else {
373
+ view
374
+ }
375
+ case .drawOff:
376
+ if #available(iOS 26.0, tvOS 26.0, *) {
377
+ view.symbolEffect(buildDrawOffEffect(config), options: options, isActive: isActive)
378
+ } else {
379
+ view
380
+ }
381
+ }
382
+ }
383
+
384
+ @available(iOS 17.0, tvOS 17.0, *)
385
+ @ViewBuilder
386
+ private func applyDiscreteEffect<TargetView: View>(
387
+ to view: TargetView,
388
+ config: SymbolEffectConfig,
389
+ options: SymbolEffectOptions,
390
+ value: AnyHashable
391
+ ) -> some View {
392
+ switch config.effect {
393
+ case .pulse:
394
+ view.symbolEffect(buildPulseEffect(config), options: options, value: value)
395
+ case .bounce:
396
+ view.symbolEffect(buildBounceEffect(config), options: options, value: value)
397
+ case .variableColor:
398
+ view.symbolEffect(buildVariableColorEffect(config), options: options, value: value)
399
+ case .wiggle:
400
+ if #available(iOS 18.0, tvOS 18.0, *) {
401
+ view.symbolEffect(buildWiggleEffect(config), options: options, value: value)
402
+ } else {
403
+ view
404
+ }
405
+ case .breathe:
406
+ if #available(iOS 18.0, tvOS 18.0, *) {
407
+ view.symbolEffect(buildBreatheEffect(config), options: options, value: value)
408
+ } else {
409
+ view
410
+ }
411
+ case .rotate:
412
+ if #available(iOS 18.0, tvOS 18.0, *) {
413
+ view.symbolEffect(buildRotateEffect(config), options: options, value: value)
414
+ } else {
415
+ view
416
+ }
417
+ // Scale, Appear, Disappear, DrawOn, DrawOff don't conform to
418
+ // DiscreteSymbolEffect — they no-op when bound to a `value`.
419
+ case .scale, .appear, .disappear, .drawOn, .drawOff:
420
+ view
421
+ }
422
+ }
423
+
424
+ // MARK: - Discrete wrapper
425
+
426
+ @available(iOS 17.0, tvOS 17.0, *)
427
+ private struct DiscreteEffectView<WrappedContent: View>: View {
428
+ let config: SymbolEffectConfig
429
+ let options: SymbolEffectOptions
430
+ @ObservedObject var state: ObservableState
431
+ @ViewBuilder let content: () -> WrappedContent
432
+
433
+ var body: some View {
434
+ let trigger = (state.value as? AnyHashable) ?? AnyHashable(0)
435
+ applyDiscreteEffect(to: content(), config: config, options: options, value: trigger)
436
+ }
437
+ }
438
+
439
+ // MARK: - Indefinite wrapper
440
+
441
+ @available(iOS 17.0, tvOS 17.0, *)
442
+ private struct IndefiniteEffectView<WrappedContent: View>: View {
443
+ let config: SymbolEffectConfig
444
+ let options: SymbolEffectOptions
445
+ @ObservedObject var state: ObservableState
446
+ @ViewBuilder let content: () -> WrappedContent
447
+
448
+ var body: some View {
449
+ let active = (state.value as? Bool) ?? true
450
+ applyIndefiniteEffect(to: content(), config: config, options: options, isActive: active)
451
+ }
452
+ }
@@ -1957,5 +1957,9 @@ extension ViewModifierRegistry {
1957
1957
  register("containerBackground") { params, appContext, _ in
1958
1958
  return try ContainerBackgroundModifier(from: params, appContext: appContext)
1959
1959
  }
1960
+
1961
+ register("symbolEffect") { params, appContext, _ in
1962
+ return try SymbolEffectModifier(from: params, appContext: appContext)
1963
+ }
1960
1964
  }
1961
1965
  }
@@ -5,6 +5,7 @@ import ExpoModulesCore
5
5
 
6
6
  internal final class SlotViewProps: ExpoSwiftUI.ViewProps {
7
7
  @Field var name: String = ""
8
+ @Field var extraProps: [String: Any]?
8
9
  }
9
10
 
10
11
  internal struct SlotView: ExpoSwiftUI.View {
@@ -17,6 +18,10 @@ internal struct SlotView: ExpoSwiftUI.View {
17
18
  var body: some View {
18
19
  Children()
19
20
  }
21
+
22
+ func extra<T>(_ key: String, as type: T.Type = T.self) -> T? {
23
+ return props.extraProps?[key] as? T
24
+ }
20
25
  }
21
26
 
22
27
  extension [any ExpoSwiftUI.AnyChild] {
@@ -0,0 +1 @@
1
+ fed75afc6bc0231fa3cf65ec40bfbbbec4ee7203
@@ -0,0 +1 @@
1
+ 23ba3e9f955c4fbbc30d2d18e007b303692c56ab229f64092e2a21417c7b6fb3
@@ -0,0 +1 @@
1
+ 8e7857cfe9376c2a4fdda8902bdaa2c516b53b38366623f8b474f2dc518b39d7988490e8ef011302ec9443d0e60823ba61dadc4b2a2a836e0c576ee81ab817c8
@@ -0,0 +1 @@
1
+ 2d7e475d6bf490db454783e814b6d7bdf3a3656a
@@ -0,0 +1 @@
1
+ 16d55d07ff8c3287a906b606ab76bf0e2757895d0cf7777a97588daed1804943
@@ -0,0 +1 @@
1
+ 8111a057a0454d1b10993b2fa75487a3c81d9a991aea915c827f4be17e240bac8114a7e3f7d4403302f97f68e255a52d3348f1cc83529dcba5bcb31b67295542
@@ -3,7 +3,7 @@
3
3
  "component": {
4
4
  "group": "expo.modules.ui",
5
5
  "module": "expo.modules.ui",
6
- "version": "56.0.6",
6
+ "version": "56.0.8",
7
7
  "attributes": {
8
8
  "org.gradle.status": "release"
9
9
  }
@@ -24,13 +24,13 @@
24
24
  },
25
25
  "files": [
26
26
  {
27
- "name": "expo.modules.ui-56.0.6.aar",
28
- "url": "expo.modules.ui-56.0.6.aar",
29
- "size": 1741087,
30
- "sha512": "9153e9187cc172ce1793a149742c785d8a21bdb184999f83942f74a9eec67fac4526c6710563a45ce99d43773f1e2e2b2a7d11fedf03653a5d3125c66900c85e",
31
- "sha256": "034e7b27268544deeae53969a3c6edb7dccaa9648517de96383f64de4735b61d",
32
- "sha1": "28dcb1963deda39da1d284330ae45e0c900c24bd",
33
- "md5": "69d6e33780c1406aa3d7dace9bdb5302"
27
+ "name": "expo.modules.ui-56.0.8.aar",
28
+ "url": "expo.modules.ui-56.0.8.aar",
29
+ "size": 1742873,
30
+ "sha512": "8111a057a0454d1b10993b2fa75487a3c81d9a991aea915c827f4be17e240bac8114a7e3f7d4403302f97f68e255a52d3348f1cc83529dcba5bcb31b67295542",
31
+ "sha256": "16d55d07ff8c3287a906b606ab76bf0e2757895d0cf7777a97588daed1804943",
32
+ "sha1": "2d7e475d6bf490db454783e814b6d7bdf3a3656a",
33
+ "md5": "7d65df12a162b1158d8d3b62582b8ce9"
34
34
  }
35
35
  ]
36
36
  },
@@ -141,13 +141,13 @@
141
141
  ],
142
142
  "files": [
143
143
  {
144
- "name": "expo.modules.ui-56.0.6.aar",
145
- "url": "expo.modules.ui-56.0.6.aar",
146
- "size": 1741087,
147
- "sha512": "9153e9187cc172ce1793a149742c785d8a21bdb184999f83942f74a9eec67fac4526c6710563a45ce99d43773f1e2e2b2a7d11fedf03653a5d3125c66900c85e",
148
- "sha256": "034e7b27268544deeae53969a3c6edb7dccaa9648517de96383f64de4735b61d",
149
- "sha1": "28dcb1963deda39da1d284330ae45e0c900c24bd",
150
- "md5": "69d6e33780c1406aa3d7dace9bdb5302"
144
+ "name": "expo.modules.ui-56.0.8.aar",
145
+ "url": "expo.modules.ui-56.0.8.aar",
146
+ "size": 1742873,
147
+ "sha512": "8111a057a0454d1b10993b2fa75487a3c81d9a991aea915c827f4be17e240bac8114a7e3f7d4403302f97f68e255a52d3348f1cc83529dcba5bcb31b67295542",
148
+ "sha256": "16d55d07ff8c3287a906b606ab76bf0e2757895d0cf7777a97588daed1804943",
149
+ "sha1": "2d7e475d6bf490db454783e814b6d7bdf3a3656a",
150
+ "md5": "7d65df12a162b1158d8d3b62582b8ce9"
151
151
  }
152
152
  ]
153
153
  },
@@ -161,13 +161,13 @@
161
161
  },
162
162
  "files": [
163
163
  {
164
- "name": "expo.modules.ui-56.0.6-sources.jar",
165
- "url": "expo.modules.ui-56.0.6-sources.jar",
166
- "size": 82267,
167
- "sha512": "26e067865e2bbdd9f770d670f8212c2a309bee05009f722931b339ca9f76c235bbed2286a876d9c7a7b0ddb0e4b152617031f487c047ae78d0672ecc2649c6be",
168
- "sha256": "213a1693673392a710d27fef37d99ef030c8a8783b4ccaa971ea914588dc5241",
169
- "sha1": "a534f795e8ea46629011b2f8939608619b954ff1",
170
- "md5": "f6beb7095321eea7da6845fa478235b0"
164
+ "name": "expo.modules.ui-56.0.8-sources.jar",
165
+ "url": "expo.modules.ui-56.0.8-sources.jar",
166
+ "size": 83317,
167
+ "sha512": "8e7857cfe9376c2a4fdda8902bdaa2c516b53b38366623f8b474f2dc518b39d7988490e8ef011302ec9443d0e60823ba61dadc4b2a2a836e0c576ee81ab817c8",
168
+ "sha256": "23ba3e9f955c4fbbc30d2d18e007b303692c56ab229f64092e2a21417c7b6fb3",
169
+ "sha1": "fed75afc6bc0231fa3cf65ec40bfbbbec4ee7203",
170
+ "md5": "7f944d9b9a5ef111622ffba70c2e3dce"
171
171
  }
172
172
  ]
173
173
  }
@@ -0,0 +1 @@
1
+ 03025ed3157b46b01ba571a909f21eac4feb3a7e
@@ -0,0 +1 @@
1
+ ab31c528b625bc59841826409dd7bb7cdbc0d5f409ff8dcb190880cec1c1d986
@@ -0,0 +1 @@
1
+ e559c715dcb00aa217865d1b2633bc4f317bfef47174d2ee9234d17e31869732a44099f5c73d66d93218315e30a1a1249f1198f21b41c1adf25381cbc64b31f1
@@ -9,7 +9,7 @@
9
9
  <modelVersion>4.0.0</modelVersion>
10
10
  <groupId>expo.modules.ui</groupId>
11
11
  <artifactId>expo.modules.ui</artifactId>
12
- <version>56.0.6</version>
12
+ <version>56.0.8</version>
13
13
  <packaging>aar</packaging>
14
14
  <name>expo.modules.ui</name>
15
15
  <url>https://github.com/expo/expo</url>
@@ -0,0 +1 @@
1
+ a8b115099ec51743ae12b97d7b21cc1d3f91f8ae
@@ -0,0 +1 @@
1
+ bb1b7c427fd33b05ff5660ecd4d87f5c8c657c22196d808b5c5cb7d5cad63ce2
@@ -0,0 +1 @@
1
+ 1014f718571005419a686ac582920962ece0cbca9e308306868688a483a99935ce2f34ad1f08130f08dc36decf7787e3886bf79848d9409818fd1398d4ec4c17
@@ -3,11 +3,11 @@
3
3
  <groupId>expo.modules.ui</groupId>
4
4
  <artifactId>expo.modules.ui</artifactId>
5
5
  <versioning>
6
- <latest>56.0.6</latest>
7
- <release>56.0.6</release>
6
+ <latest>56.0.8</latest>
7
+ <release>56.0.8</release>
8
8
  <versions>
9
- <version>56.0.6</version>
9
+ <version>56.0.8</version>
10
10
  </versions>
11
- <lastUpdated>20260513103326</lastUpdated>
11
+ <lastUpdated>20260515152508</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- b3ca330a5f2396c76ef2a75c6a63a1b5
1
+ 07f7ee65741318dec532e697349df568
@@ -1 +1 @@
1
- 056c42f73a3ce70484fd129941ba605b4b43b1a2
1
+ 0812a12fc3cc429e4a46d7001a5db9fa11faa5fa
@@ -1 +1 @@
1
- 51b027c8f2849c17546bf8aa17f35a8707289abfd20627996422c4cf2287115e
1
+ 4611d64d0e92ad43d8a487a107ee0422cff861f119287ec7e62fd006bf77ad3e
@@ -1 +1 @@
1
- b7ac9cf92e7dbf7455874551e21d961c47e7df4031554f03584b4c67ad1e64e8510ad7a4a99f1ab39a4bd945a94f162f43153c49b16a851b398e6d42505cd8b1
1
+ 4dff8d7ddda323e9df5ee3545288bede281b6a8d89687d580cfdc3502bf3a101ee1fcd95913fb8a5d761f109c74f00354f2e9b8b9d87177bc6f898777fb8b6a9