aix 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +32 -1
  2. package/ios/HybridAix.swift +74 -6
  3. package/ios/HybridAixComposer.swift +63 -24
  4. package/lib/commonjs/aix.js +6 -2
  5. package/lib/commonjs/aix.js.map +1 -1
  6. package/lib/module/aix.js +6 -2
  7. package/lib/module/aix.js.map +1 -1
  8. package/lib/typescript/src/aix.d.ts +235 -11
  9. package/lib/typescript/src/aix.d.ts.map +1 -1
  10. package/lib/typescript/src/views/aix.nitro.d.ts +14 -0
  11. package/lib/typescript/src/views/aix.nitro.d.ts.map +1 -1
  12. package/nitrogen/generated/android/AixOnLoad.cpp +2 -0
  13. package/nitrogen/generated/android/c++/JAixStickToKeyboard.hpp +63 -0
  14. package/nitrogen/generated/android/c++/JAixStickToKeyboardOffset.hpp +61 -0
  15. package/nitrogen/generated/android/c++/JFunc_void_bool.hpp +75 -0
  16. package/nitrogen/generated/android/c++/JHybridAixComposerSpec.cpp +18 -3
  17. package/nitrogen/generated/android/c++/JHybridAixComposerSpec.hpp +2 -1
  18. package/nitrogen/generated/android/c++/JHybridAixSpec.cpp +18 -0
  19. package/nitrogen/generated/android/c++/JHybridAixSpec.hpp +2 -0
  20. package/nitrogen/generated/android/c++/views/JHybridAixComposerStateUpdater.cpp +4 -1
  21. package/nitrogen/generated/android/c++/views/JHybridAixStateUpdater.cpp +4 -0
  22. package/nitrogen/generated/android/kotlin/com/margelo/nitro/aix/AixStickToKeyboard.kt +41 -0
  23. package/nitrogen/generated/android/kotlin/com/margelo/nitro/aix/AixStickToKeyboardOffset.kt +41 -0
  24. package/nitrogen/generated/android/kotlin/com/margelo/nitro/aix/Func_void_bool.kt +80 -0
  25. package/nitrogen/generated/android/kotlin/com/margelo/nitro/aix/HybridAixComposerSpec.kt +5 -1
  26. package/nitrogen/generated/android/kotlin/com/margelo/nitro/aix/HybridAixSpec.kt +14 -0
  27. package/nitrogen/generated/ios/Aix-Swift-Cxx-Bridge.cpp +8 -0
  28. package/nitrogen/generated/ios/Aix-Swift-Cxx-Bridge.hpp +73 -0
  29. package/nitrogen/generated/ios/Aix-Swift-Cxx-Umbrella.hpp +6 -0
  30. package/nitrogen/generated/ios/c++/HybridAixComposerSpecSwift.hpp +14 -3
  31. package/nitrogen/generated/ios/c++/HybridAixSpecSwift.hpp +7 -0
  32. package/nitrogen/generated/ios/c++/views/HybridAixComponent.mm +5 -0
  33. package/nitrogen/generated/ios/c++/views/HybridAixComposerComponent.mm +5 -1
  34. package/nitrogen/generated/ios/swift/AixStickToKeyboard.swift +59 -0
  35. package/nitrogen/generated/ios/swift/AixStickToKeyboardOffset.swift +47 -0
  36. package/nitrogen/generated/ios/swift/Func_void_bool.swift +47 -0
  37. package/nitrogen/generated/ios/swift/HybridAixComposerSpec.swift +1 -1
  38. package/nitrogen/generated/ios/swift/HybridAixComposerSpec_cxx.swift +16 -1
  39. package/nitrogen/generated/ios/swift/HybridAixSpec.swift +1 -0
  40. package/nitrogen/generated/ios/swift/HybridAixSpec_cxx.swift +32 -0
  41. package/nitrogen/generated/shared/c++/AixStickToKeyboard.hpp +81 -0
  42. package/nitrogen/generated/shared/c++/AixStickToKeyboardOffset.hpp +79 -0
  43. package/nitrogen/generated/shared/c++/HybridAixComposerSpec.cpp +2 -1
  44. package/nitrogen/generated/shared/c++/HybridAixComposerSpec.hpp +6 -3
  45. package/nitrogen/generated/shared/c++/HybridAixSpec.cpp +2 -0
  46. package/nitrogen/generated/shared/c++/HybridAixSpec.hpp +2 -0
  47. package/nitrogen/generated/shared/c++/views/HybridAixComponent.cpp +12 -0
  48. package/nitrogen/generated/shared/c++/views/HybridAixComponent.hpp +1 -0
  49. package/nitrogen/generated/shared/c++/views/HybridAixComposerComponent.cpp +12 -0
  50. package/nitrogen/generated/shared/c++/views/HybridAixComposerComponent.hpp +3 -1
  51. package/nitrogen/generated/shared/json/AixComposerConfig.json +1 -0
  52. package/nitrogen/generated/shared/json/AixConfig.json +1 -0
  53. package/package.json +6 -6
  54. package/src/aix.tsx +8 -1
  55. package/src/views/aix.nitro.ts +18 -1
package/README.md CHANGED
@@ -97,7 +97,8 @@ The main container component that provides keyboard-aware behavior and manages s
97
97
  |------|------|---------|-------------|
98
98
  | `shouldStartAtEnd` | `boolean` | - | Whether the scroll view should start scrolled to the end of the content. |
99
99
  | `scrollOnFooterSizeUpdate` | `object` | `{ enabled: true, scrolledToEndThreshold: 100, animated: false }` | Control the behavior of scrolling when the footer size changes. By default, changing the height of the footer will shift content up in the scroll view. |
100
- | `scrollEndReachedThreshold` | `number` | `max(blankSize, 200)` | The number of pixels from the bottom of the scroll view to the end of the content that is considered "near the end". Used to determine if content should shift up when keyboard opens. |
100
+ | `scrollEndReachedThreshold` | `number` | `max(blankSize, 200)` | The number of pixels from the bottom of the scroll view to the end of the content that is considered "near the end". Used by `onScrolledNearEndChange` and to determine if content should shift up when keyboard opens. |
101
+ | `onScrolledNearEndChange` | `(isNearEnd: boolean) => void` | - | Callback fired when the scroll position transitions between "near end" and "not near end" states. Reactive to user scrolling, content size changes, parent size changes, and keyboard height changes. Uses `scrollEndReachedThreshold` to determine the threshold. |
101
102
  | `additionalContentInsets` | `object` | - | Additional content insets applied when keyboard is open or closed. Shape: `{ top?: { whenKeyboardOpen, whenKeyboardClosed }, bottom?: { whenKeyboardOpen, whenKeyboardClosed } }` |
102
103
  | `additionalScrollIndicatorInsets` | `object` | - | Additional insets for the scroll indicator, added to existing safe area insets. Applied to `verticalScrollIndicatorInsets` on iOS. |
103
104
  | `mainScrollViewID` | `string` | - | The `nativeID` of the scroll view to use. If provided, will search for a scroll view with this `accessibilityIdentifier`. |
@@ -180,6 +181,36 @@ function Chat({ messages }) {
180
181
  }
181
182
  ```
182
183
 
184
+ ---
185
+
186
+ ### Scroll to End Button
187
+
188
+ You can use `onScrolledNearEndChange` to show a "scroll to end" button when the user scrolls away from the bottom:
189
+
190
+ ```tsx
191
+ import { Aix, useAixRef } from 'aix';
192
+ import { useState } from 'react';
193
+
194
+ function Chat() {
195
+ const aix = useAixRef();
196
+ const [isNearEnd, setIsNearEnd] = useState(false);
197
+
198
+ return (
199
+ <Aix
200
+ ref={aix}
201
+ scrollEndReachedThreshold={200}
202
+ onScrolledNearEndChange={setIsNearEnd}
203
+ >
204
+ {/* ScrollView and messages... */}
205
+
206
+ {!isNearEnd && (
207
+ <Button onPress={() => aix.current?.scrollToEnd(true)} />
208
+ )}
209
+ </Aix>
210
+ );
211
+ }
212
+ ```
213
+
183
214
  ## TODOs
184
215
 
185
216
 
@@ -15,19 +15,19 @@ private var aixContextKey: UInt8 = 0
15
15
  protocol AixContext: AnyObject {
16
16
  /// The blank view (last cell) - used for calculating blank size
17
17
  var blankView: HybridAixCellView? { get set }
18
-
18
+
19
19
  /// The composer view
20
20
  var composerView: HybridAixComposer? { get set }
21
-
21
+
22
22
  /// Called when the blank view's size changes
23
23
  func reportBlankViewSizeChange(size: CGSize, index: Int)
24
-
24
+
25
25
  /// Register a cell with the context
26
26
  func registerCell(_ cell: HybridAixCellView)
27
-
27
+
28
28
  /// Unregister a cell from the context
29
29
  func unregisterCell(_ cell: HybridAixCellView)
30
-
30
+
31
31
  /// Register the composer view
32
32
  func registerComposerView(_ composerView: HybridAixComposer)
33
33
 
@@ -36,6 +36,14 @@ protocol AixContext: AnyObject {
36
36
 
37
37
  /// Called when the composer's height changes
38
38
  func reportComposerHeightChange(height: CGFloat)
39
+
40
+ // MARK: - Keyboard State (for composer sticky behavior)
41
+
42
+ /// Current keyboard height
43
+ var keyboardHeight: CGFloat { get }
44
+
45
+ /// Keyboard height when fully open (for calculating progress)
46
+ var keyboardHeightWhenOpen: CGFloat { get }
39
47
  }
40
48
 
41
49
  extension UIView {
@@ -93,6 +101,7 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
93
101
 
94
102
  var shouldApplyContentInsets: Bool? = nil
95
103
  var onWillApplyContentInsets: ((_ insets: AixContentInsets) -> Void)? = nil
104
+ var onScrolledNearEndChange: ((_ isNearEnd: Bool) -> Void)? = nil
96
105
 
97
106
  var additionalContentInsets: AixAdditionalContentInsetsProp?
98
107
 
@@ -199,6 +208,14 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
199
208
  /// Flag to track if we're currently in an interactive keyboard dismiss
200
209
  private var isInInteractiveDismiss = false
201
210
 
211
+ /// Previous "scrolled near end" state for change detection
212
+ private var prevIsScrolledNearEnd: Bool? = nil
213
+
214
+ /// KVO observation tokens for scroll view
215
+ private var contentOffsetObservation: NSKeyValueObservation?
216
+ private var contentSizeObservation: NSKeyValueObservation?
217
+ private var boundsObservation: NSKeyValueObservation?
218
+
202
219
  // MARK: - Context References (weak to avoid retain cycles)
203
220
 
204
221
  weak var blankView: HybridAixCellView? = nil {
@@ -244,12 +261,41 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
244
261
  scrollView.automaticallyAdjustsScrollIndicatorInsets = false
245
262
 
246
263
  setupPanGestureObserver()
264
+ setupScrollViewObservers(scrollView)
247
265
  applyScrollIndicatorInsets()
248
266
  }
249
267
 
250
268
  return sv
251
269
  }
252
270
 
271
+ /// Set up KVO observers on scroll view for contentOffset, contentSize, and bounds changes
272
+ private func setupScrollViewObservers(_ scrollView: UIScrollView) {
273
+ // Observe contentOffset (user scrolling)
274
+ contentOffsetObservation = scrollView.observe(\.contentOffset, options: [.new]) { [weak self] _, _ in
275
+ self?.updateScrolledNearEndState()
276
+ }
277
+
278
+ // Observe contentSize (content size changes)
279
+ contentSizeObservation = scrollView.observe(\.contentSize, options: [.new]) { [weak self] _, _ in
280
+ self?.updateScrolledNearEndState()
281
+ }
282
+
283
+ // Observe bounds (parent size changes)
284
+ boundsObservation = scrollView.observe(\.bounds, options: [.new]) { [weak self] _, _ in
285
+ self?.updateScrolledNearEndState()
286
+ }
287
+ }
288
+
289
+ /// Clean up KVO observers
290
+ private func removeScrollViewObservers() {
291
+ contentOffsetObservation?.invalidate()
292
+ contentOffsetObservation = nil
293
+ contentSizeObservation?.invalidate()
294
+ contentSizeObservation = nil
295
+ boundsObservation?.invalidate()
296
+ boundsObservation = nil
297
+ }
298
+
253
299
  /// Set up observer on scroll view's pan gesture to detect interactive keyboard dismiss
254
300
  private func setupPanGestureObserver() {
255
301
  guard let scrollView = cachedScrollView else { return }
@@ -416,6 +462,19 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
416
462
  if scrollView.contentInset.bottom != targetBottom {
417
463
  scrollView.contentInset.bottom = targetBottom
418
464
  }
465
+
466
+ // Update scrolled near end state after insets change
467
+ updateScrolledNearEndState()
468
+ }
469
+
470
+ /// Centralized function to check and fire onScrolledNearEndChange callback
471
+ /// Called from KVO observers and after content inset changes
472
+ private func updateScrolledNearEndState() {
473
+ guard scrollView != nil else { return }
474
+ let isNearEnd = getIsScrolledNearEnd(distFromEnd: distFromEnd)
475
+ guard isNearEnd != prevIsScrolledNearEnd else { return }
476
+ prevIsScrolledNearEnd = isNearEnd
477
+ onScrolledNearEndChange?(isNearEnd)
419
478
  }
420
479
 
421
480
  /// Apply scroll indicator insets to the scroll view
@@ -568,9 +627,12 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
568
627
  startEvent = event
569
628
  }
570
629
  }
571
-
630
+
572
631
  // Update keyboard state
573
632
  handleKeyboardMove(height: height, progress: progress)
633
+
634
+ // Update composer transform
635
+ composerView?.applyKeyboardTransform(height: height, heightWhenOpen: keyboardHeightWhenOpen, animated: false)
574
636
  }
575
637
 
576
638
  // MARK: - Initialization
@@ -587,6 +649,7 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
587
649
 
588
650
  deinit {
589
651
  removePanGestureObserver()
652
+ removeScrollViewObservers()
590
653
  }
591
654
 
592
655
  // MARK: - Lifecycle
@@ -657,6 +720,7 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
657
720
  applyContentInset()
658
721
  applyScrollIndicatorInsets()
659
722
  scrollToEndInternal(animated: false)
723
+ updateScrolledNearEndState()
660
724
  }
661
725
  didScrollToEndInitially = true
662
726
  } else {
@@ -812,6 +876,7 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
812
876
  keyboardProgress = 1.0
813
877
  applyContentInset()
814
878
  applyScrollIndicatorInsets()
879
+ composerView?.applyKeyboardTransform(height: targetHeight, heightWhenOpen: keyboardHeightWhenOpen, animated: false)
815
880
  return
816
881
  }
817
882
 
@@ -826,6 +891,7 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
826
891
  }
827
892
  self.applyContentInset()
828
893
  self.applyScrollIndicatorInsets()
894
+ self.composerView?.applyKeyboardTransform(height: targetHeight, heightWhenOpen: self.keyboardHeightWhenOpen, animated: false)
829
895
 
830
896
  if let (startY, endY) = self.startEvent?.interpolateContentOffsetY {
831
897
  self.scrollView?.setContentOffset(CGPoint(x: 0, y: endY), animated: false)
@@ -853,6 +919,7 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
853
919
  self.keyboardProgress = 0
854
920
  self.applyContentInset()
855
921
  self.applyScrollIndicatorInsets()
922
+ self.composerView?.applyKeyboardTransform(height: 0, heightWhenOpen: self.keyboardHeightWhenOpen, animated: false)
856
923
  }, completion: { [weak self] _ in
857
924
  self?.handleKeyboardDidMove(height: 0, progress: 0)
858
925
  })
@@ -865,6 +932,7 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
865
932
  func keyboardDidHide(notification: NSNotification) {
866
933
  print("[Aix] keyboardDidHide")
867
934
  keyboardHeightWhenOpen = 0
935
+ composerView?.applyKeyboardTransform(height: 0, heightWhenOpen: 0, animated: false)
868
936
  }
869
937
 
870
938
  func keyboardWillChangeFrame(notification: NSNotification) {
@@ -12,23 +12,34 @@ import UIKit
12
12
  /// It registers itself with the AixContext so the context can track composer height
13
13
  /// for calculating content insets
14
14
  class HybridAixComposer: HybridAixComposerSpec {
15
-
15
+
16
+ // MARK: - Props
17
+
18
+ var stickToKeyboard: AixStickToKeyboard? {
19
+ didSet {
20
+ // When stickToKeyboard changes, re-apply transform with current keyboard state
21
+ if let ctx = cachedAixContext {
22
+ applyKeyboardTransform(height: ctx.keyboardHeight, heightWhenOpen: ctx.keyboardHeightWhenOpen, animated: false)
23
+ }
24
+ }
25
+ }
26
+
16
27
  // MARK: - Inner View
17
-
28
+
18
29
  /// Custom UIView that notifies owner when layout changes
19
30
  private final class InnerView: UIView {
20
31
  weak var owner: HybridAixComposer?
21
-
32
+
22
33
  override func layoutSubviews() {
23
34
  super.layoutSubviews()
24
35
  owner?.handleLayoutChange()
25
36
  }
26
-
37
+
27
38
  override func didMoveToWindow() {
28
39
  super.didMoveToWindow()
29
40
  owner?.handleDidMoveToWindow()
30
41
  }
31
-
42
+
32
43
  override func willMove(toSuperview newSuperview: UIView?) {
33
44
  super.willMove(toSuperview: newSuperview)
34
45
  if newSuperview == nil {
@@ -36,37 +47,37 @@ class HybridAixComposer: HybridAixComposerSpec {
36
47
  owner?.handleWillRemoveFromSuperview()
37
48
  }
38
49
  }
39
-
50
+
40
51
  // MARK: - Touch Handling
41
-
52
+
42
53
  /// Let touches pass through to React Native children
43
54
  override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
44
55
  return false
45
56
  }
46
57
  }
47
-
58
+
48
59
  // MARK: - Properties
49
-
60
+
50
61
  /// The UIView for this composer
51
62
  let view: UIView
52
-
63
+
53
64
  /// Cached reference to the AixContext (found on first access)
54
65
  private weak var cachedAixContext: AixContext?
55
66
 
56
67
  /// Last reported height (to avoid reporting unchanged heights)
57
68
  private var lastReportedHeight: CGFloat = 0
58
-
69
+
59
70
  // MARK: - Initialization
60
-
71
+
61
72
  override init() {
62
73
  let inner = InnerView()
63
74
  self.view = inner
64
75
  super.init()
65
76
  inner.owner = self
66
77
  }
67
-
78
+
68
79
  // MARK: - Context Access
69
-
80
+
70
81
  /// Get the AixContext, caching it for performance
71
82
  private func getAixContext() -> AixContext? {
72
83
  if let cached = cachedAixContext {
@@ -76,22 +87,26 @@ class HybridAixComposer: HybridAixComposerSpec {
76
87
  cachedAixContext = ctx
77
88
  return ctx
78
89
  }
79
-
90
+
80
91
  // MARK: - Lifecycle Handlers
81
-
92
+
82
93
  /// Called when the view is added to a window (full hierarchy is connected)
83
94
  private func handleDidMoveToWindow() {
84
- guard view.window != nil else { return }
85
-
95
+ guard view.window != nil else {
96
+ return
97
+ }
98
+
86
99
  // Clear cached context since hierarchy changed
87
100
  cachedAixContext = nil
88
-
101
+
89
102
  // Register with the new context
90
103
  if let ctx = getAixContext() {
91
104
  ctx.registerComposerView(self)
105
+ // Initial state
106
+ applyKeyboardTransform(height: ctx.keyboardHeight, heightWhenOpen: ctx.keyboardHeightWhenOpen, animated: false)
92
107
  }
93
108
  }
94
-
109
+
95
110
  /// Called when the view is about to be removed from superview
96
111
  private func handleWillRemoveFromSuperview() {
97
112
  // Unregister from context before removal
@@ -100,7 +115,7 @@ class HybridAixComposer: HybridAixComposerSpec {
100
115
  }
101
116
  cachedAixContext = nil
102
117
  }
103
-
118
+
104
119
  /// Called when layoutSubviews fires (size may have changed)
105
120
  private func handleLayoutChange() {
106
121
  let currentHeight = view.bounds.height
@@ -109,11 +124,35 @@ class HybridAixComposer: HybridAixComposerSpec {
109
124
  getAixContext()?.reportComposerHeightChange(height: currentHeight)
110
125
  }
111
126
  }
112
-
127
+
128
+ // MARK: - Keyboard handling
129
+
130
+ /// Apply keyboard transform to move the composer with the keyboard
131
+ /// Called by the AixContext when keyboard state changes
132
+ func applyKeyboardTransform(height: CGFloat, heightWhenOpen: CGFloat, animated: Bool) {
133
+ guard let targetView = view.superview else {
134
+ return
135
+ }
136
+
137
+ guard let settings = stickToKeyboard, settings.enabled else {
138
+ targetView.transform = .identity
139
+ return
140
+ }
141
+
142
+ let progress = heightWhenOpen > 0 ? height / heightWhenOpen : 0
143
+
144
+ let offsetWhenClosed = CGFloat(settings.offset?.whenKeyboardClosed ?? 0)
145
+ let offsetWhenOpen = CGFloat(settings.offset?.whenKeyboardOpen ?? 0)
146
+
147
+ let currentOffset = offsetWhenClosed + (offsetWhenOpen - offsetWhenClosed) * progress
148
+
149
+ let translateY = -height - currentOffset
150
+
151
+ targetView.transform = CGAffineTransform(translationX: 0, y: translateY)
152
+ }
153
+
113
154
  // MARK: - Deinitialization
114
-
115
155
  deinit {
116
- // Ensure we unregister when deallocated
117
156
  cachedAixContext?.unregisterComposerView(self)
118
157
  }
119
158
  }
@@ -7,9 +7,10 @@ exports.Aix = void 0;
7
7
  var _reactNativeNitroModules = require("react-native-nitro-modules");
8
8
  var _AixConfig = _interopRequireDefault(require("../nitrogen/generated/shared/json/AixConfig.json"));
9
9
  var _react = require("react");
10
+ var _reactNativeReanimated = _interopRequireDefault(require("react-native-reanimated"));
10
11
  var _jsxRuntime = require("react/jsx-runtime");
11
12
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
- const AixInternal = (0, _reactNativeNitroModules.getHostComponent)('Aix', () => _AixConfig.default);
13
+ const AixInternal = _reactNativeReanimated.default.createAnimatedComponent((0, _reactNativeNitroModules.getHostComponent)('Aix', () => _AixConfig.default));
13
14
 
14
15
  // User-facing props type that accepts regular functions (not wrapped callbacks)
15
16
 
@@ -23,7 +24,10 @@ const Aix = exports.Aix = /*#__PURE__*/(0, _react.forwardRef)(function Aix(props
23
24
  }
24
25
  // Wrap onWillApplyContentInsets with callback() if provided
25
26
  ,
26
- onWillApplyContentInsets: props.onWillApplyContentInsets ? (0, _reactNativeNitroModules.callback)(props.onWillApplyContentInsets) : undefined,
27
+ onWillApplyContentInsets: props.onWillApplyContentInsets ? (0, _reactNativeNitroModules.callback)(props.onWillApplyContentInsets) : undefined
28
+ // Wrap onScrolledNearEndChange with callback() if provided
29
+ ,
30
+ onScrolledNearEndChange: props.onScrolledNearEndChange ? (0, _reactNativeNitroModules.callback)(props.onScrolledNearEndChange) : undefined,
27
31
  hybridRef: ref ? (0, _reactNativeNitroModules.callback)(r => {
28
32
  if (typeof ref === 'function') {
29
33
  ref(r);
@@ -1 +1 @@
1
- {"version":3,"names":["_reactNativeNitroModules","require","_AixConfig","_interopRequireDefault","_react","_jsxRuntime","e","__esModule","default","AixInternal","getHostComponent","AixConfig","Aix","exports","forwardRef","props","ref","jsx","scrollOnFooterSizeUpdate","enabled","scrolledToEndThreshold","animated","onWillApplyContentInsets","callback","undefined","hybridRef","r","current"],"sourceRoot":"../../src","sources":["aix.tsx"],"mappings":";;;;;;AAAA,IAAAA,wBAAA,GAAAC,OAAA;AAKA,IAAAC,UAAA,GAAAC,sBAAA,CAAAF,OAAA;AAEA,IAAAG,MAAA,GAAAH,OAAA;AAAuD,IAAAI,WAAA,GAAAJ,OAAA;AAAA,SAAAE,uBAAAG,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAIvD,MAAMG,WAAW,GAAG,IAAAC,yCAAgB,EAClC,KAAK,EACL,MAAMC,kBACR,CAAC;;AAED;;AAQO,MAAMC,GAAG,GAAAC,OAAA,CAAAD,GAAA,gBAAG,IAAAE,iBAAU,EAC3B,SAASF,GAAGA,CAACG,KAAK,EAAEC,GAAG,EAAE;EACvB,oBACE,IAAAX,WAAA,CAAAY,GAAA,EAACR,WAAW;IAAA,GACNM,KAAK;IACTG,wBAAwB,EACtBH,KAAK,CAACG,wBAAwB,IAAI;MAChCC,OAAO,EAAE,IAAI;MACbC,sBAAsB,EAAE,GAAG;MAC3BC,QAAQ,EAAE;IACZ;IAEF;IAAA;IACAC,wBAAwB,EACtBP,KAAK,CAACO,wBAAwB,GAC1B,IAAAC,iCAAQ,EAACR,KAAK,CAACO,wBAAwB,CAAC,GACxCE,SACL;IACDC,SAAS,EACPT,GAAG,GACC,IAAAO,iCAAQ,EAAEG,CAAC,IAAK;MACd,IAAI,OAAOV,GAAG,KAAK,UAAU,EAAE;QAC7BA,GAAG,CAACU,CAAC,CAAC;MACR,CAAC,MAAM;QACLV,GAAG,CAACW,OAAO,GAAGD,CAAC;MACjB;IACF,CAAC,CAAC,GACFF;EACL,CACF,CAAC;AAEN,CACF,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["_reactNativeNitroModules","require","_AixConfig","_interopRequireDefault","_react","_reactNativeReanimated","_jsxRuntime","e","__esModule","default","AixInternal","Animated","createAnimatedComponent","getHostComponent","AixConfig","Aix","exports","forwardRef","props","ref","jsx","scrollOnFooterSizeUpdate","enabled","scrolledToEndThreshold","animated","onWillApplyContentInsets","callback","undefined","onScrolledNearEndChange","hybridRef","r","current"],"sourceRoot":"../../src","sources":["aix.tsx"],"mappings":";;;;;;AAAA,IAAAA,wBAAA,GAAAC,OAAA;AAKA,IAAAC,UAAA,GAAAC,sBAAA,CAAAF,OAAA;AAEA,IAAAG,MAAA,GAAAH,OAAA;AACA,IAAAI,sBAAA,GAAAF,sBAAA,CAAAF,OAAA;AAA8C,IAAAK,WAAA,GAAAL,OAAA;AAAA,SAAAE,uBAAAI,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAI9C,MAAMG,WAAW,GAAGC,8BAAQ,CAACC,uBAAuB,CAAC,IAAAC,yCAAgB,EACnE,KAAK,EACL,MAAMC,kBACR,CAAC,CAAC;;AAEF;;AASO,MAAMC,GAAG,GAAAC,OAAA,CAAAD,GAAA,gBAAG,IAAAE,iBAAU,EAC3B,SAASF,GAAGA,CAACG,KAAK,EAAEC,GAAG,EAAE;EACvB,oBACE,IAAAb,WAAA,CAAAc,GAAA,EAACV,WAAW;IAAA,GACNQ,KAAK;IACTG,wBAAwB,EACtBH,KAAK,CAACG,wBAAwB,IAAI;MAChCC,OAAO,EAAE,IAAI;MACbC,sBAAsB,EAAE,GAAG;MAC3BC,QAAQ,EAAE;IACZ;IAEF;IAAA;IACAC,wBAAwB,EACtBP,KAAK,CAACO,wBAAwB,GAC1B,IAAAC,iCAAQ,EAACR,KAAK,CAACO,wBAAwB,CAAC,GACxCE;IAEN;IAAA;IACAC,uBAAuB,EACrBV,KAAK,CAACU,uBAAuB,GACzB,IAAAF,iCAAQ,EAACR,KAAK,CAACU,uBAAuB,CAAC,GACvCD,SACL;IACDE,SAAS,EACPV,GAAG,GACC,IAAAO,iCAAQ,EAAEI,CAAC,IAAK;MAChB,IAAI,OAAOX,GAAG,KAAK,UAAU,EAAE;QAC7BA,GAAG,CAACW,CAAC,CAAC;MACR,CAAC,MAAM;QACLX,GAAG,CAACY,OAAO,GAAGD,CAAC;MACjB;IACF,CAAC,CAAC,GACAH;EACL,CACF,CAAC;AAEN,CACF,CAAC","ignoreList":[]}
package/lib/module/aix.js CHANGED
@@ -3,8 +3,9 @@
3
3
  import { callback, getHostComponent } from 'react-native-nitro-modules';
4
4
  import AixConfig from '../nitrogen/generated/shared/json/AixConfig.json';
5
5
  import { forwardRef } from 'react';
6
+ import Animated from 'react-native-reanimated';
6
7
  import { jsx as _jsx } from "react/jsx-runtime";
7
- const AixInternal = getHostComponent('Aix', () => AixConfig);
8
+ const AixInternal = Animated.createAnimatedComponent(getHostComponent('Aix', () => AixConfig));
8
9
 
9
10
  // User-facing props type that accepts regular functions (not wrapped callbacks)
10
11
 
@@ -18,7 +19,10 @@ export const Aix = /*#__PURE__*/forwardRef(function Aix(props, ref) {
18
19
  }
19
20
  // Wrap onWillApplyContentInsets with callback() if provided
20
21
  ,
21
- onWillApplyContentInsets: props.onWillApplyContentInsets ? callback(props.onWillApplyContentInsets) : undefined,
22
+ onWillApplyContentInsets: props.onWillApplyContentInsets ? callback(props.onWillApplyContentInsets) : undefined
23
+ // Wrap onScrolledNearEndChange with callback() if provided
24
+ ,
25
+ onScrolledNearEndChange: props.onScrolledNearEndChange ? callback(props.onScrolledNearEndChange) : undefined,
22
26
  hybridRef: ref ? callback(r => {
23
27
  if (typeof ref === 'function') {
24
28
  ref(r);
@@ -1 +1 @@
1
- {"version":3,"names":["callback","getHostComponent","AixConfig","forwardRef","jsx","_jsx","AixInternal","Aix","props","ref","scrollOnFooterSizeUpdate","enabled","scrolledToEndThreshold","animated","onWillApplyContentInsets","undefined","hybridRef","r","current"],"sourceRoot":"../../src","sources":["aix.tsx"],"mappings":";;AAAA,SACEA,QAAQ,EACRC,gBAAgB,QAEX,4BAA4B;AACnC,OAAOC,SAAS,MAAM,kDAAkD;AAExE,SAASC,UAAU,QAA6B,OAAO;AAAA,SAAAC,GAAA,IAAAC,IAAA;AAIvD,MAAMC,WAAW,GAAGL,gBAAgB,CAClC,KAAK,EACL,MAAMC,SACR,CAAC;;AAED;;AAQA,OAAO,MAAMK,GAAG,gBAAGJ,UAAU,CAC3B,SAASI,GAAGA,CAACC,KAAK,EAAEC,GAAG,EAAE;EACvB,oBACEJ,IAAA,CAACC,WAAW;IAAA,GACNE,KAAK;IACTE,wBAAwB,EACtBF,KAAK,CAACE,wBAAwB,IAAI;MAChCC,OAAO,EAAE,IAAI;MACbC,sBAAsB,EAAE,GAAG;MAC3BC,QAAQ,EAAE;IACZ;IAEF;IAAA;IACAC,wBAAwB,EACtBN,KAAK,CAACM,wBAAwB,GAC1Bd,QAAQ,CAACQ,KAAK,CAACM,wBAAwB,CAAC,GACxCC,SACL;IACDC,SAAS,EACPP,GAAG,GACCT,QAAQ,CAAEiB,CAAC,IAAK;MACd,IAAI,OAAOR,GAAG,KAAK,UAAU,EAAE;QAC7BA,GAAG,CAACQ,CAAC,CAAC;MACR,CAAC,MAAM;QACLR,GAAG,CAACS,OAAO,GAAGD,CAAC;MACjB;IACF,CAAC,CAAC,GACFF;EACL,CACF,CAAC;AAEN,CACF,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["callback","getHostComponent","AixConfig","forwardRef","Animated","jsx","_jsx","AixInternal","createAnimatedComponent","Aix","props","ref","scrollOnFooterSizeUpdate","enabled","scrolledToEndThreshold","animated","onWillApplyContentInsets","undefined","onScrolledNearEndChange","hybridRef","r","current"],"sourceRoot":"../../src","sources":["aix.tsx"],"mappings":";;AAAA,SACEA,QAAQ,EACRC,gBAAgB,QAEX,4BAA4B;AACnC,OAAOC,SAAS,MAAM,kDAAkD;AAExE,SAASC,UAAU,QAA6B,OAAO;AACvD,OAAOC,QAAQ,MAAM,yBAAyB;AAAA,SAAAC,GAAA,IAAAC,IAAA;AAI9C,MAAMC,WAAW,GAAGH,QAAQ,CAACI,uBAAuB,CAACP,gBAAgB,CACnE,KAAK,EACL,MAAMC,SACR,CAAC,CAAC;;AAEF;;AASA,OAAO,MAAMO,GAAG,gBAAGN,UAAU,CAC3B,SAASM,GAAGA,CAACC,KAAK,EAAEC,GAAG,EAAE;EACvB,oBACEL,IAAA,CAACC,WAAW;IAAA,GACNG,KAAK;IACTE,wBAAwB,EACtBF,KAAK,CAACE,wBAAwB,IAAI;MAChCC,OAAO,EAAE,IAAI;MACbC,sBAAsB,EAAE,GAAG;MAC3BC,QAAQ,EAAE;IACZ;IAEF;IAAA;IACAC,wBAAwB,EACtBN,KAAK,CAACM,wBAAwB,GAC1BhB,QAAQ,CAACU,KAAK,CAACM,wBAAwB,CAAC,GACxCC;IAEN;IAAA;IACAC,uBAAuB,EACrBR,KAAK,CAACQ,uBAAuB,GACzBlB,QAAQ,CAACU,KAAK,CAACQ,uBAAuB,CAAC,GACvCD,SACL;IACDE,SAAS,EACPR,GAAG,GACCX,QAAQ,CAAEoB,CAAC,IAAK;MAChB,IAAI,OAAOT,GAAG,KAAK,UAAU,EAAE;QAC7BA,GAAG,CAACS,CAAC,CAAC;MACR,CAAC,MAAM;QACLT,GAAG,CAACU,OAAO,GAAGD,CAAC;MACjB;IACF,CAAC,CAAC,GACAH;EACL,CACF,CAAC;AAEN,CACF,CAAC","ignoreList":[]}