@symbiote-native/components 1.0.0 → 3.0.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 (110) hide show
  1. package/README.md +11 -13
  2. package/build/accessibility-props.d.ts +1 -1
  3. package/build/accessibility-props.js +2 -2
  4. package/build/behaviors/activity-indicator/index.android.d.ts +1 -0
  5. package/build/behaviors/activity-indicator/index.android.js +16 -0
  6. package/build/behaviors/activity-indicator/index.d.ts +3 -0
  7. package/build/behaviors/activity-indicator/index.ios.d.ts +1 -0
  8. package/build/behaviors/activity-indicator/index.ios.js +14 -0
  9. package/build/behaviors/activity-indicator/index.js +5 -0
  10. package/build/behaviors/activity-indicator/shared.d.ts +18 -0
  11. package/build/behaviors/activity-indicator/shared.js +120 -0
  12. package/build/behaviors/button.d.ts +13 -0
  13. package/build/behaviors/button.js +340 -0
  14. package/build/behaviors/image-background.d.ts +3 -0
  15. package/build/behaviors/image-background.js +155 -0
  16. package/build/behaviors/image.d.ts +1 -2
  17. package/build/behaviors/image.js +25 -106
  18. package/build/behaviors/input-accessory-view.d.ts +1 -2
  19. package/build/behaviors/input-accessory-view.js +49 -55
  20. package/build/behaviors/pressable.d.ts +59 -1
  21. package/build/behaviors/pressable.js +142 -96
  22. package/build/behaviors/refresh-control.d.ts +2 -0
  23. package/build/behaviors/refresh-control.js +96 -0
  24. package/build/behaviors/scroll-view/index.android.d.ts +1 -0
  25. package/build/behaviors/scroll-view/index.android.js +40 -0
  26. package/build/behaviors/scroll-view/index.d.ts +3 -0
  27. package/build/behaviors/scroll-view/index.ios.d.ts +1 -0
  28. package/build/behaviors/scroll-view/index.ios.js +10 -0
  29. package/build/behaviors/scroll-view/index.js +8 -0
  30. package/build/behaviors/scroll-view/responder.d.ts +4 -0
  31. package/build/behaviors/scroll-view/responder.js +202 -0
  32. package/build/behaviors/scroll-view/shared.d.ts +9 -0
  33. package/build/behaviors/scroll-view/shared.js +291 -0
  34. package/build/behaviors/scroll-view/sticky.d.ts +18 -0
  35. package/build/behaviors/scroll-view/sticky.js +581 -0
  36. package/build/behaviors/switch.d.ts +1 -1
  37. package/build/behaviors/switch.js +49 -88
  38. package/build/behaviors/text-input.d.ts +2 -2
  39. package/build/behaviors/text-input.js +244 -104
  40. package/build/behaviors/touchable-highlight.d.ts +9 -0
  41. package/build/behaviors/touchable-highlight.js +205 -0
  42. package/build/behaviors/touchable-native-feedback.d.ts +20 -0
  43. package/build/behaviors/touchable-native-feedback.js +254 -0
  44. package/build/behaviors/touchable-opacity.d.ts +12 -0
  45. package/build/behaviors/touchable-opacity.js +239 -0
  46. package/build/behaviors/touchable-without-feedback.d.ts +2 -0
  47. package/build/behaviors/touchable-without-feedback.js +231 -0
  48. package/build/component-names/index.android.js +31 -25
  49. package/build/component-names/index.ios.js +28 -23
  50. package/build/component-names/shared.d.ts +2 -1
  51. package/build/component-names/shared.js +16 -6
  52. package/build/descriptor.js +4 -4
  53. package/build/index.d.ts +26 -26
  54. package/build/index.js +56 -27
  55. package/build/register.d.ts +1 -0
  56. package/build/register.js +55 -0
  57. package/build/resolve-intrinsic.js +3 -9
  58. package/build/scroll-view-commands.d.ts +1 -5
  59. package/build/scroll-view-commands.js +23 -85
  60. package/build/state/flat-list.d.ts +2 -2
  61. package/build/state/flat-list.js +10 -2
  62. package/build/state/pressable.d.ts +6 -1
  63. package/build/state/pressable.js +63 -28
  64. package/build/state/section-list.d.ts +2 -0
  65. package/build/state/section-list.js +14 -7
  66. package/build/state/text-input.d.ts +10 -40
  67. package/build/state/text-input.js +17 -186
  68. package/build/state/touchable.d.ts +1 -0
  69. package/build/state/touchable.js +11 -8
  70. package/build/state/virtualized-list-reducer.d.ts +2 -2
  71. package/build/state/virtualized-list.d.ts +6 -6
  72. package/build/state/virtualized-list.js +71 -37
  73. package/build/text-props.d.ts +0 -8
  74. package/build/text-props.js +14 -25
  75. package/build/view/render-button.d.ts +11 -4
  76. package/build/view/render-button.js +74 -22
  77. package/build/view/render-image/index.d.ts +14 -1
  78. package/build/view/render-image/index.js +22 -147
  79. package/build/view/render-input-accessory-view.d.ts +1 -5
  80. package/build/view/render-input-accessory-view.js +26 -48
  81. package/build/view/render-keyboard-avoiding-view.d.ts +7 -1
  82. package/build/view/render-keyboard-avoiding-view.js +40 -1
  83. package/build/view/render-modal.d.ts +1 -1
  84. package/build/view/render-modal.js +18 -8
  85. package/build/view/render-pressable/index.d.ts +3 -0
  86. package/build/view/render-pressable/index.js +28 -0
  87. package/build/view/render-scroll-view.d.ts +1 -4
  88. package/build/view/render-scroll-view.js +17 -54
  89. package/build/view/render-switch.d.ts +4 -15
  90. package/build/view/render-switch.js +4 -42
  91. package/build/view/render-touchable-highlight.d.ts +1 -0
  92. package/build/view/render-touchable-native-feedback.d.ts +19 -1
  93. package/build/view/render-touchable-native-feedback.js +34 -9
  94. package/host-primitives.cjs +178 -280
  95. package/host-primitives.d.cts +0 -3
  96. package/package.json +8 -21
  97. package/build/fold-host-bag.d.ts +0 -15
  98. package/build/fold-host-bag.js +0 -99
  99. package/build/state-style.d.ts +0 -15
  100. package/build/state-style.js +0 -47
  101. package/build/view/render-activity-indicator.d.ts +0 -25
  102. package/build/view/render-activity-indicator.js +0 -88
  103. package/build/view/render-image-background.d.ts +0 -9
  104. package/build/view/render-image-background.js +0 -48
  105. package/build/view/render-text-input.d.ts +0 -11
  106. package/build/view/render-text-input.js +0 -39
  107. package/lowering-fixtures.cjs +0 -259
  108. package/lowering-fixtures.d.cts +0 -17
  109. package/specialize-state-style.cjs +0 -219
  110. package/specialize-state-style.d.cts +0 -15
@@ -7,7 +7,7 @@
7
7
  // The only text-laying intrinsic; drives the RCTText / RCTVirtualText nesting choice
8
8
  // (a <Text> inside another <Text> becomes a virtual span). Platform-invariant, so it is
9
9
  // not part of the per-platform name table.
10
- const TEXT_INTRINSICS = new Set(['symbiote-text']);
10
+ const TEXT_INTRINSICS = new Set(['text']);
11
11
  // Assemble the descriptor map a platform file exports: each intrinsic's Fabric name from
12
12
  // the platform table, paired with its invariant isText flag.
13
13
  export function buildDescriptors(names) {
@@ -32,12 +32,23 @@ export function makeDescriptorFor(descriptors) {
32
32
  const descriptor = descriptors[type];
33
33
  if (descriptor !== undefined)
34
34
  return descriptor;
35
- if (type.startsWith('symbiote-')) {
36
- throw new Error(`Unknown symbiote component type: ${type}`);
37
- }
35
+ // NO "unknown tag of ours" throw any more, and the reason is the whole cost of dropping the
36
+ // prefix: `symbiote-*` was a MARKER, so a miss carrying it could only be our own typo. Without
37
+ // it this namespace holds three populations that a string cannot tell apart —
38
+ //
39
+ // view · scroll-view ours
40
+ // counter-child · app-root an Angular app's OWN component selectors, kebab by convention
41
+ // RCTView · RNCSlider a Fabric view name, resolving through the fallthrough below
42
+ //
43
+ // — and the first attempt at a replacement ("lowercase is ours") threw on the second row, which
44
+ // is most of an Angular app. Measured: 222 tests, the primary failure being `counter-child`.
45
+ //
46
+ // What replaces it is stronger and is not runtime at all: `ISymbioteIntrinsic` is a closed
47
+ // union, so a misspelled tag in our own source is a compile error at every call site that names
48
+ // one. The guard was only ever a backstop for a name built dynamically.
38
49
  if (unrewritten.has(type)) {
39
50
  throw new Error(`"${type}" is a primitive's PUBLIC name, not a Fabric view name — a rewrite was missed. ` +
40
- `Expected the intrinsic tag (e.g. "symbiote-view"). Falling through would have committed ` +
51
+ `Expected the intrinsic tag (e.g. "view"). Falling through would have committed ` +
41
52
  `a Fabric view literally named "${type}", which fails on device only.`);
42
53
  }
43
54
  return { component: type, isText: false };
@@ -64,7 +75,6 @@ function publicNamesThatAreNotViewNames(descriptors) {
64
75
  const names = new Set();
65
76
  for (const intrinsic of Object.keys(descriptors)) {
66
77
  const publicName = intrinsic
67
- .slice('symbiote-'.length)
68
78
  .split('-')
69
79
  .map(part => part.charAt(0).toUpperCase() + part.slice(1))
70
80
  .join('');
@@ -4,14 +4,14 @@
4
4
  // adapter's host element then flows on through its reconciler → engine → Fabric.
5
5
  //
6
6
  // `type` is an open host-component string, not a closed two-member union, because
7
- // symbiote paints one host element PER native component (`symbiote-activity-indicator`,
8
- // `symbiote-switch`, …), not just a generic box/text pair.
9
- // el(): a host element of any type. txt(): shorthand for the `symbiote-text` primitive.
7
+ // symbiote paints one host element PER native component (`activity-indicator`,
8
+ // `switch`, …), not just a generic box/text pair.
9
+ // el(): a host element of any type. txt(): shorthand for the `text` primitive.
10
10
  export function el(type, props = {}, children = [], key) {
11
11
  return { type, props, children, key };
12
12
  }
13
13
  export function txt(props = {}, children = []) {
14
- return { type: 'symbiote-text', props, children };
14
+ return { type: 'text', props, children };
15
15
  }
16
16
  // `bridge` names the caller (`descriptorToSolid`, `descriptorToSvelte`) so a device log says which
17
17
  // one tripped without a stack to read.
package/build/index.d.ts CHANGED
@@ -3,22 +3,18 @@ export type { IDescriptor, IDescriptorType, IDescriptorProps, IDescriptorChild,
3
3
  export { createDescriptorShapeGuard } from './descriptor';
4
4
  export type { IDescriptorShapeGuard } from './descriptor';
5
5
  export { resolveAccessibilityProps } from './accessibility-props';
6
- export { resolveTextProps } from './text-props';
7
- export type { IEllipsizeMode, ITextDefaultableProps } from './text-props';
6
+ export type { IEllipsizeMode } from './text-props';
8
7
  export type { IAccessibilityProps, IAriaProps, IAccessibilityRole, IRole, IAccessibilityStateValue, IAccessibilityValue, IAccessibilityActionInfo, } from './accessibility-props';
9
8
  export { descriptorFor, COMPONENT_DESCRIPTORS } from './component-names';
10
9
  export { buildDescriptors, makeDescriptorFor } from './component-names/shared';
11
- export type { ISymbioteIntrinsic, IComponentDescriptor, } from './component-names/shared';
12
- export { renderActivityIndicator } from './view/render-activity-indicator';
13
- export type { IActivityIndicatorProps, IActivityIndicatorViewProps, IActivityIndicatorPlatform, IActivityIndicatorSize, } from './view/render-activity-indicator';
14
- export { renderSwitch } from './view/render-switch';
15
- export type { ISwitchProps, ISwitchViewProps, ISwitchPlatform, ISwitchTrackColor, } from './view/render-switch';
10
+ export type { ISymbioteIntrinsic, IComponentDescriptor, ICrossTypedIntrinsics, } from './component-names/shared';
11
+ export type { ISwitchProps, ISwitchTrackColor, ISwitchChangeEvent, } from './view/render-switch';
16
12
  export type { IResponderProps } from './responder-props';
17
13
  export { switchReducer, createInitialSwitchState, shouldSnapBack, valueFromChange, } from './state/switch';
18
14
  export type { ISwitchState, ISwitchAction } from './state/switch';
19
- export { resolveDecelerationRate, selectScrollIntrinsics, readLayoutDimension, didContentSizeChange, resolveScrollForwarding, SCROLL_VIEW_BASE_HORIZONTAL, SCROLL_VIEW_BASE_VERTICAL, } from './view/render-scroll-view';
15
+ export { selectScrollIntrinsics, readLayoutDimension, didContentSizeChange, resolveScrollForwarding, } from './view/render-scroll-view';
20
16
  export type { IScrollIntrinsics, IContentSize, IScrollForwarding, IScrollForwardingInputs, IScrollForwardMode, } from './view/render-scroll-view';
21
- export { buildScrollViewHandle, splitLayoutProps, attachStickyScroll, isSymbioteEvent, forwardScrollEvent, } from './scroll-view-commands';
17
+ export { buildScrollViewHandle, attachStickyScroll, isSymbioteEvent, forwardScrollEvent, } from './scroll-view-commands';
22
18
  export type { IScrollViewHandle } from './scroll-view-commands';
23
19
  export { computeStickyInterpolation, nextStickyHeaderY, stickyDebounceMs, readLayoutNumber, STICKY_HEADER_Z_INDEX, } from './view/render-scroll-sticky';
24
20
  export type { IStickyHeaderProps, IStickyInterpolationParams, } from './view/render-scroll-sticky';
@@ -28,33 +24,27 @@ export type { IImageSource, IImageSourceProp, IResizeMode, IImageProps, IImageVi
28
24
  export { renderImage } from './view/render-image';
29
25
  export type { IImageStatics, IImageSize, IImageCacheStatus, } from '@symbiote-native/engine';
30
26
  export { imageStatics, setImageSourceResolver } from '@symbiote-native/engine';
31
- export { renderImageBackground } from './view/render-image-background';
32
- export type { IImageBackgroundViewProps } from './view/render-image-background';
33
- export { renderInputAccessoryView } from './view/render-input-accessory-view';
34
27
  export type { IInputAccessoryViewViewProps } from './view/render-input-accessory-view';
35
28
  export { renderModal } from './view/render-modal';
36
29
  export type { IModalViewProps, IModalAnimationType, IModalPresentationStyle, IModalOrientation, IModalOrientationChangeEvent, } from './view/render-modal';
37
30
  export { modalReducer, createInitialModalState, shouldRenderModal, } from './state/modal';
38
31
  export type { IModalState, IModalAction } from './state/modal';
39
- export { computeInset, keyboardAvoidingEventNamesFor, readPrefersCrossFadeTransitions, readKeyboardFrame, readLayoutFrame, resolveKeyboardAvoidingLayout, DEFAULT_VERTICAL_OFFSET, } from './view/render-keyboard-avoiding-view';
40
- export type { IComputeInsetOptions, IKeyboardAvoidingBehavior, IKeyboardAvoidingEventNames, IMeasuredFrame, IKeyboardFrame, IKeyboardAvoidingLayout, IResolveKeyboardAvoidingLayoutParams, } from './view/render-keyboard-avoiding-view';
41
- export { createPressHandlers, createPressRuntime, disposePressRuntime, normalizeRect, maxEdge, isTouchWithinRegion, readPoint, computeRegion, rippleProps, DEFAULT_DELAY_LONG_PRESS_MS, DEFAULT_MIN_PRESS_DURATION_MS, DEFAULT_PRESS_RECT_OFFSETS, } from './state/pressable';
32
+ export { computeInset, configureKeyboardAvoidingAnimation, keyboardAvoidingEventNamesFor, readKeyboardAnimationTiming, readPrefersCrossFadeTransitions, readKeyboardFrame, readLayoutFrame, resolveKeyboardAvoidingLayout, DEFAULT_VERTICAL_OFFSET, } from './view/render-keyboard-avoiding-view';
33
+ export type { IComputeInsetOptions, IKeyboardAnimationTiming, IKeyboardAvoidingBehavior, IKeyboardAvoidingEventNames, IMeasuredFrame, IKeyboardFrame, IKeyboardAvoidingLayout, IResolveKeyboardAvoidingLayoutParams, } from './view/render-keyboard-avoiding-view';
34
+ export { createPressHandlers, createPressRuntime, disposePressRuntime, normalizeRect, maxEdge, isTouchWithinRegion, readPoint, computeRegion, DEFAULT_DELAY_LONG_PRESS_MS, DEFAULT_MIN_PRESS_DURATION_MS, DEFAULT_PRESS_RECT_OFFSETS, } from './state/pressable';
42
35
  export type { IPressState, IPressHandler, IPressHandlers, IPressHost, IPressRuntime, IPressMachineConfig, IFrameCallback, IRectOffset, IEdgeInsets, IResponderRegion, IPressableAndroidRippleConfig, IRippleBackground as IPressableRippleBackground, } from './state/pressable';
43
- export { buildPressableListeners, resolveDisabledAccessibilityState, noteHoverNoop, shouldSuppressPress, shouldClaimResponder, isTerminationAllowed, } from './view/render-pressable';
36
+ export { buildPressableListeners, resolveDisabledAccessibilityState, noteHoverNoop, shouldSuppressPress, shouldClaimResponder, isTerminationAllowed, resolvePressableFocusable, resolveTouchableFocusable, } from './view/render-pressable';
44
37
  export { computePressOutWait, createTouchableFeedbackRuntime, createTouchableFeedbackHandlers, DEFAULT_ACTIVE_OPACITY, OPACITY_ACTIVE_GRANT_DURATION_MS, OPACITY_ACTIVE_DURATION_MS, OPACITY_INACTIVE_DURATION_MS, RESTING_OPACITY, DEFAULT_HIGHLIGHT_CHILD_OPACITY, DEFAULT_UNDERLAY_COLOR, TOUCHABLE_MIN_PRESS_DURATION_MS, restingOpacityFromStyle, hasTouchablePressHandler, createHighlightUnderlayRuntime, createHighlightUnderlayHandlers, } from './state/touchable';
45
38
  export type { IPressTimingProps, ITouchableHandler, ITouchableFeedbackRuntime, ITouchableFeedbackConfig, ITouchableFeedbackCallbacks, ITouchableFeedbackHandlers, ITouchablePressHandlerProps, IHighlightUnderlayRuntime, IHighlightUnderlayConfig, IHighlightUnderlayCallbacks, IHighlightUnderlayHandlers, } from './state/touchable';
46
39
  export { resolveHighlightExtraStyles } from './view/render-touchable-highlight';
47
40
  export type { ITouchableHighlightExtraStyles, ITouchableHighlightUnderlayView, } from './view/render-touchable-highlight';
48
- export { backgroundProps, canUseNativeForeground, selectableBackground, selectableBackgroundBorderless, rippleBackground, } from './view/render-touchable-native-feedback';
41
+ export { canUseNativeForeground, selectableBackground, selectableBackgroundBorderless, rippleBackground, TouchableNativeFeedback, } from './view/render-touchable-native-feedback';
49
42
  export type { INativeFeedbackBackground, IThemeAttrBackground, IRippleBackground, } from './view/render-touchable-native-feedback';
50
- export { BUTTON_ACCESSIBILITY_ROLE, buttonTextStyle, resolveButtonTextStyle, } from './view/render-button';
43
+ export { resolveButtonDisabled } from './view/render-button';
51
44
  export type { IButtonProps } from './view/render-button';
52
- export { resolveTextInputProps, foldText, foldAutoComplete, foldSubmitBehavior, mapAutoComplete, textFromChange, eventCountFromChange, shouldCommandText, INITIAL_EVENT_COUNT, SELECTION_NONE, } from './state/text-input';
53
- export type { ITextInputProps, ITextInputHandle, ITextInputSelection, ITextInputEventHandler, ITextInputFoldInput, IFoldedTextInputProps, IInputMode, IEnterKeyHint, ISubmitBehavior, } from './state/text-input';
54
- export { keyboardTypeForInputMode } from './state/text-input';
55
- export { renderTextInput } from './view/render-text-input';
56
- export type { ITextInputViewProps } from './view/render-text-input';
57
- export { DEFAULT_WINDOW_SIZE, DEFAULT_INITIAL_NUM_TO_RENDER, DEFAULT_END_REACHED_THRESHOLD, DEFAULT_MAX_TO_RENDER_PER_BATCH, DEFAULT_UPDATE_CELLS_BATCHING_PERIOD, DEFAULT_VIEW_AREA_COVERAGE_PERCENT_THRESHOLD, DEFAULT_START_REACHED_THRESHOLD, FIRST_INDEX, EMPTY_OFFSET, NO_INDEX, NO_CONTENT_LENGTH_SENT, INVERTED_Y_STYLE, INVERTED_X_STYLE, readScrollOffset, readLayoutLength, readLayoutOffset, buildOffsets, computeWindow, throttleWindow, visiblePercent, isCellViewable, offsetForIndex, averageMeasuredLength, averageMeasuredStride, highestMeasuredIndex, computeEndReached, computeStartReached, buildViewabilityPairs, computeViewableSet, diffViewable, maxMinimumViewTime, buildListPlan, computeMvcpAdjustment, resolveItemKey, indexOfItem, offsetForEnd, isSeparatorGapInRange, decideEdgeReached, resolveStickySectionHeaders, wrapFixedLayout, resolveAverageLength, } from './state/virtualized-list';
45
+ export { foldText, textFromChange, eventCountFromChange, shouldCommandText, INITIAL_EVENT_COUNT, SELECTION_NONE, } from './state/text-input';
46
+ export type { ITextInputProps, ITextInputHandle, ITextInputSelection, ITextInputEventHandler, IInputMode, IEnterKeyHint, ISubmitBehavior, ITextInputChangeEvent, } from './state/text-input';
47
+ export { DEFAULT_WINDOW_SIZE, DEFAULT_INITIAL_NUM_TO_RENDER, DEFAULT_MAX_TO_RENDER_PER_BATCH, DEFAULT_UPDATE_CELLS_BATCHING_PERIOD, DEFAULT_VIEW_AREA_COVERAGE_PERCENT_THRESHOLD, FIRST_INDEX, EMPTY_OFFSET, NO_INDEX, NO_CONTENT_LENGTH_SENT, INVERTED_Y_STYLE, INVERTED_X_STYLE, readScrollOffset, readLayoutLength, readLayoutOffset, buildOffsets, computeWindow, throttleWindow, isCellViewable, offsetForIndex, averageMeasuredLength, averageMeasuredStride, highestMeasuredIndex, computeEndReached, computeStartReached, buildViewabilityPairs, computeViewableSet, diffViewable, maxMinimumViewTime, buildListPlan, computeMvcpAdjustment, resolveItemKey, indexOfItem, offsetForEnd, isSeparatorGapInRange, decideEdgeReached, resolveStickySectionHeaders, wrapFixedLayout, resolveAverageLength, } from './state/virtualized-list';
58
48
  export type { IMvcpAction, IMvcpAdjustmentParams, IMvcpAdjustmentResult, ICellLayout, ISeparators, ISeparatorProps, IViewToken, IViewableItemsChangedInfo, IViewabilityConfig, IViewabilityConfigCallbackPair, IVirtualizedListHandle, IViewableSetParams, IListCellPlan, IListPlan, IListPlanParams, } from './state/virtualized-list';
59
49
  export { reduceList, createInitialListState, listEffectSignature, } from './state/virtualized-list-reducer';
60
50
  export type { IListState, IListMetrics, IListReducerInputs, IListAction, IListEffect, IListReduceResult, } from './state/virtualized-list-reducer';
@@ -65,7 +55,17 @@ export type { IRow } from './state/flat-list';
65
55
  export { flattenSections, unwrapEntryItem, sectionEntryKey, scrollLocationToFlatIndex, } from './state/section-list';
66
56
  export type { ISection, ISectionEntry, IVirtualizedSectionListHandle, } from './state/section-list';
67
57
  export { registerPressableBehavior, PRESSABLE_TAG, } from './behaviors/pressable';
68
- export { foldImagePayload, IMAGE_TAG, registerImageBehavior, } from './behaviors/image';
69
- export { foldInputAccessoryViewPayload, INPUT_ACCESSORY_VIEW_TAG, registerInputAccessoryViewBehavior, } from './behaviors/input-accessory-view';
58
+ export { registerTouchableOpacityBehavior, TOUCHABLE_OPACITY_TAG, } from './behaviors/touchable-opacity';
59
+ export { registerTouchableHighlightBehavior, TOUCHABLE_HIGHLIGHT_TAG, } from './behaviors/touchable-highlight';
60
+ export { registerButtonBehavior, BUTTON_TAG } from './behaviors/button';
61
+ export { registerTouchableNativeFeedbackBehavior, TOUCHABLE_NATIVE_FEEDBACK_TAG, } from './behaviors/touchable-native-feedback';
62
+ export { registerTouchableWithoutFeedbackBehavior, TOUCHABLE_WITHOUT_FEEDBACK_TAG, } from './behaviors/touchable-without-feedback';
63
+ export { ACTIVITY_INDICATOR_SPINNER_TAG, ACTIVITY_INDICATOR_TAG, registerActivityIndicatorBehavior, } from './behaviors/activity-indicator';
64
+ export type { IActivityIndicatorProps, IActivityIndicatorSize, } from './behaviors/activity-indicator';
65
+ export { IMAGE_TAG, registerImageBehavior } from './behaviors/image';
66
+ export { IMAGE_BACKGROUND_TAG, registerImageBackgroundBehavior, } from './behaviors/image-background';
67
+ export { INPUT_ACCESSORY_VIEW_TAG, registerInputAccessoryViewBehavior, } from './behaviors/input-accessory-view';
70
68
  export { registerTextInputBehavior, buildTextInputHandle, TEXT_INPUT_TAG, TEXT_INPUT_MULTILINE_TAG, } from './behaviors/text-input';
71
69
  export { registerSwitchBehavior, SWITCH_TAG } from './behaviors/switch';
70
+ export { registerRefreshControlBehavior, REFRESH_CONTROL_TAG, } from './behaviors/refresh-control';
71
+ export { HORIZONTAL_SCROLL_VIEW_TAG, registerScrollViewBehavior, SCROLL_VIEW_TAG, STICKY_HEADER_TAG, } from './behaviors/scroll-view';
package/build/index.js CHANGED
@@ -11,23 +11,20 @@ export { createDescriptorShapeGuard } from './descriptor.js';
11
11
  // and its types. Framework-agnostic, so React, Vue, and the next adapter all fold
12
12
  // identically; moved here from @symbiote-native/react. RefreshControl/SafeAreaView/ScrollView consume it.
13
13
  export { resolveAccessibilityProps } from './accessibility-props.js';
14
- export { resolveTextProps } from './text-props.js';
15
14
  // Intrinsic (`symbiote-*`) → Fabric view-name resolution. Shared by every adapter so the
16
15
  // names CANNOT drift between them (one engine, one Fabric). The name tables are
17
16
  // Metro-split (.ios/.android, filename selects, no Platform.OS read); the base
18
17
  // re-exports iOS for headless. descriptorFor is the per-platform-bound resolver.
19
18
  export { descriptorFor, COMPONENT_DESCRIPTORS } from './component-names';
20
19
  export { buildDescriptors, makeDescriptorFor } from './component-names/shared.js';
21
- export { renderActivityIndicator } from './view/render-activity-indicator.js';
22
- export { renderSwitch } from './view/render-switch.js';
23
20
  // Switch is the first STATE machine off this layer (ActivityIndicator was render-only):
24
21
  // the reducer + the two pure predicates are the logic half, the adapter supplies the hook.
25
22
  export { switchReducer, createInitialSwitchState, shouldSnapBack, valueFromChange, } from './state/switch.js';
26
23
  // ScrollView: pure render/command helpers (no state machine, no 3-layer split). The adapter
27
24
  // owns the refs/effects/element assembly and the sticky-header component; these supply the
28
25
  // platform-invariant math and plumbing every adapter shares.
29
- export { resolveDecelerationRate, selectScrollIntrinsics, readLayoutDimension, didContentSizeChange, resolveScrollForwarding, SCROLL_VIEW_BASE_HORIZONTAL, SCROLL_VIEW_BASE_VERTICAL, } from './view/render-scroll-view.js';
30
- export { buildScrollViewHandle, splitLayoutProps, attachStickyScroll, isSymbioteEvent, forwardScrollEvent, } from './scroll-view-commands.js';
26
+ export { selectScrollIntrinsics, readLayoutDimension, didContentSizeChange, resolveScrollForwarding, } from './view/render-scroll-view.js';
27
+ export { buildScrollViewHandle, attachStickyScroll, isSymbioteEvent, forwardScrollEvent, } from './scroll-view-commands.js';
31
28
  export { computeStickyInterpolation, nextStickyHeaderY, stickyDebounceMs, readLayoutNumber, STICKY_HEADER_Z_INDEX, } from './view/render-scroll-sticky.js';
32
29
  // The per-header sticky effect machine: the one pure state machine every sticky-header consumer drives
33
30
  // (React/Vue header component, Angular header component, Angular projection wrapper), folding the
@@ -36,42 +33,42 @@ export { computeStickyInterpolation, nextStickyHeaderY, stickyDebounceMs, readLa
36
33
  export { reduceSticky, createInitialStickyState, stickyEffectSignature, } from './state/sticky-header-reducer.js';
37
34
  export { renderImage } from './view/render-image/index.js';
38
35
  export { imageStatics, setImageSourceResolver } from '@symbiote-native/engine';
39
- // ImageBackground: render-only composition (absolute-fill image behind, children on top).
40
- export { renderImageBackground } from './view/render-image-background.js';
41
- // InputAccessoryView: render-only host assembly (nativeID/backgroundColor).
42
- export { renderInputAccessoryView } from './view/render-input-accessory-view.js';
43
36
  // Modal: full 3-layer split (state machine gates the iOS keep-alive frame).
44
37
  export { renderModal } from './view/render-modal.js';
45
38
  export { modalReducer, createInitialModalState, shouldRenderModal, } from './state/modal.js';
46
39
  // KeyboardAvoidingView: pure inset/behavior math; the adapter owns the Keyboard subscription.
47
- export { computeInset, keyboardAvoidingEventNamesFor, readPrefersCrossFadeTransitions, readKeyboardFrame, readLayoutFrame, resolveKeyboardAvoidingLayout, DEFAULT_VERTICAL_OFFSET, } from './view/render-keyboard-avoiding-view.js';
40
+ export { computeInset, configureKeyboardAvoidingAnimation, keyboardAvoidingEventNamesFor, readKeyboardAnimationTiming, readPrefersCrossFadeTransitions, readKeyboardFrame, readLayoutFrame, resolveKeyboardAvoidingLayout, DEFAULT_VERTICAL_OFFSET, } from './view/render-keyboard-avoiding-view.js';
48
41
  // Pressable: the press state machine (logic) + render decisions. The pure lifecycle (timers,
49
42
  // geometry, drift test, suppression flags) lives in state/pressable; the responder-listener +
50
43
  // accessibilityState + ripple render decisions in view/render-pressable. Every adapter supplies
51
44
  // only its reactive `pressed` cell + the View's measure handle (see the 3-layer split).
52
- export { createPressHandlers, createPressRuntime, disposePressRuntime, normalizeRect, maxEdge, isTouchWithinRegion, readPoint, computeRegion, rippleProps, DEFAULT_DELAY_LONG_PRESS_MS, DEFAULT_MIN_PRESS_DURATION_MS, DEFAULT_PRESS_RECT_OFFSETS, } from './state/pressable.js';
53
- export { buildPressableListeners, resolveDisabledAccessibilityState, noteHoverNoop, shouldSuppressPress, shouldClaimResponder, isTerminationAllowed, } from './view/render-pressable/index.js';
45
+ export { createPressHandlers, createPressRuntime, disposePressRuntime, normalizeRect, maxEdge, isTouchWithinRegion, readPoint, computeRegion, DEFAULT_DELAY_LONG_PRESS_MS, DEFAULT_MIN_PRESS_DURATION_MS, DEFAULT_PRESS_RECT_OFFSETS, } from './state/pressable.js';
46
+ export { buildPressableListeners, resolveDisabledAccessibilityState, noteHoverNoop, shouldSuppressPress, shouldClaimResponder, isTerminationAllowed, resolvePressableFocusable, resolveTouchableFocusable, } from './view/render-pressable/index.js';
54
47
  // Touchable*: shared press-timing constants + the deactivation-floor math (the Animated feedback
55
48
  // itself stays per-adapter), and TouchableNativeFeedback's pure static factories + background map.
56
49
  export { computePressOutWait, createTouchableFeedbackRuntime, createTouchableFeedbackHandlers, DEFAULT_ACTIVE_OPACITY, OPACITY_ACTIVE_GRANT_DURATION_MS, OPACITY_ACTIVE_DURATION_MS, OPACITY_INACTIVE_DURATION_MS, RESTING_OPACITY, DEFAULT_HIGHLIGHT_CHILD_OPACITY, DEFAULT_UNDERLAY_COLOR, TOUCHABLE_MIN_PRESS_DURATION_MS, restingOpacityFromStyle, hasTouchablePressHandler, createHighlightUnderlayRuntime, createHighlightUnderlayHandlers, } from './state/touchable.js';
57
50
  export { resolveHighlightExtraStyles } from './view/render-touchable-highlight.js';
58
- export { backgroundProps, canUseNativeForeground, selectableBackground, selectableBackgroundBorderless, rippleBackground, } from './view/render-touchable-native-feedback.js';
59
- // Button: shared base text style, role constant, and color fold (the adapter composes its own
60
- // TouchableOpacity + Text).
61
- export { BUTTON_ACCESSIBILITY_ROLE, buttonTextStyle, resolveButtonTextStyle, } from './view/render-button.js';
62
- // TextInput: the controlled-value / event-count handshake. The logic half is the pure
63
- // folds/maps + the controlled-write predicate (not a single reducer: count must re-render the
64
- // imperative handle, lastNativeText must not); the view half picks the intrinsic and maps the
65
- // resolved native props. Both shared verbatim across React and Vue; the adapter owns only the
66
- // hooks/reactivity + the imperative handle.
67
- export { resolveTextInputProps, foldText, foldAutoComplete, foldSubmitBehavior, mapAutoComplete, textFromChange, eventCountFromChange, shouldCommandText, INITIAL_EVENT_COUNT, SELECTION_NONE, } from './state/text-input.js';
68
- export { keyboardTypeForInputMode } from './state/text-input.js';
69
- export { renderTextInput } from './view/render-text-input.js';
51
+ export { canUseNativeForeground, selectableBackground, selectableBackgroundBorderless, rippleBackground,
52
+ // RN's own static namespace, the only part of the deleted wrappers with nowhere else to go: the
53
+ // element is a tag now, and a tag is a string that cannot carry `.Ripple`.
54
+ TouchableNativeFeedback, } from './view/render-touchable-native-feedback.js';
55
+ // Button: `resolveButtonDisabled` alone now — the `props.disabled ?? aria ?? state.disabled`
56
+ // precedence, which the press MACHINE reads and which is therefore still JS. Every platform fold
57
+ // Button.js performs left this barrel between 2026-09-17 and 2026-09-18 for
58
+ // `SymbioteFabricProps.cpp`; the last two, `buttonViewStyle` and `resolveButtonViewStyle`, went with
59
+ // the Android arm of the test host that finally made their branch assertable.
60
+ export { resolveButtonDisabled } from './view/render-button.js';
61
+ // TextInput: the controlled-value / event-count handshake, and ONLY that now — the machine. The
62
+ // W3C->native prop resolution that used to sit beside it is the engine's (`foldTextInputAliases`,
63
+ // `SymbioteFabricProps.cpp`), so `resolveTextInputProps` and the four lookup tables under it are
64
+ // gone rather than exported-and-unused: a tag's platform props are resolved once, natively, for
65
+ // every adapter, and a JS copy would only be a second answer to the same question.
66
+ export { foldText, textFromChange, eventCountFromChange, shouldCommandText, INITIAL_EVENT_COUNT, SELECTION_NONE, } from './state/text-input.js';
70
67
  // VirtualizedList family: the framework-agnostic windowing engine + data shapes. Lists
71
68
  // have NO view/render-*.ts (the cell content is the framework's own children, so there is
72
69
  // no Descriptor render fn) — the shared layer for lists is this STATE/logic, reused
73
70
  // verbatim by React and Vue.
74
- export { DEFAULT_WINDOW_SIZE, DEFAULT_INITIAL_NUM_TO_RENDER, DEFAULT_END_REACHED_THRESHOLD, DEFAULT_MAX_TO_RENDER_PER_BATCH, DEFAULT_UPDATE_CELLS_BATCHING_PERIOD, DEFAULT_VIEW_AREA_COVERAGE_PERCENT_THRESHOLD, DEFAULT_START_REACHED_THRESHOLD, FIRST_INDEX, EMPTY_OFFSET, NO_INDEX, NO_CONTENT_LENGTH_SENT, INVERTED_Y_STYLE, INVERTED_X_STYLE, readScrollOffset, readLayoutLength, readLayoutOffset, buildOffsets, computeWindow, throttleWindow, visiblePercent, isCellViewable, offsetForIndex, averageMeasuredLength, averageMeasuredStride, highestMeasuredIndex, computeEndReached, computeStartReached, buildViewabilityPairs, computeViewableSet, diffViewable, maxMinimumViewTime, buildListPlan, computeMvcpAdjustment, resolveItemKey, indexOfItem, offsetForEnd, isSeparatorGapInRange, decideEdgeReached, resolveStickySectionHeaders, wrapFixedLayout, resolveAverageLength, } from './state/virtualized-list.js';
71
+ export { DEFAULT_WINDOW_SIZE, DEFAULT_INITIAL_NUM_TO_RENDER, DEFAULT_MAX_TO_RENDER_PER_BATCH, DEFAULT_UPDATE_CELLS_BATCHING_PERIOD, DEFAULT_VIEW_AREA_COVERAGE_PERCENT_THRESHOLD, FIRST_INDEX, EMPTY_OFFSET, NO_INDEX, NO_CONTENT_LENGTH_SENT, INVERTED_Y_STYLE, INVERTED_X_STYLE, readScrollOffset, readLayoutLength, readLayoutOffset, buildOffsets, computeWindow, throttleWindow, isCellViewable, offsetForIndex, averageMeasuredLength, averageMeasuredStride, highestMeasuredIndex, computeEndReached, computeStartReached, buildViewabilityPairs, computeViewableSet, diffViewable, maxMinimumViewTime, buildListPlan, computeMvcpAdjustment, resolveItemKey, indexOfItem, offsetForEnd, isSeparatorGapInRange, decideEdgeReached, resolveStickySectionHeaders, wrapFixedLayout, resolveAverageLength, } from './state/virtualized-list.js';
75
72
  // The orchestration reducer: the one pure state machine every list adapter drives, folding the
76
73
  // after-commit effect skeleton (window recompute -> edge -> viewability -> initial-scroll -> MVCP)
77
74
  // out of each adapter's reactive glue. The adapter maps events to actions and runs the effects.
@@ -80,7 +77,39 @@ export { subscribeListDiagnostics } from './state/virtualized-list-diagnostics.j
80
77
  export { SINGLE_COLUMN, chunkIntoRows, rowKeyExtractor, expandRowToken, expandRowViewability, lastItemOfRow, firstItemOfRow, } from './state/flat-list.js';
81
78
  export { flattenSections, unwrapEntryItem, sectionEntryKey, scrollLocationToFlatIndex, } from './state/section-list.js';
82
79
  export { registerPressableBehavior, PRESSABLE_TAG, } from './behaviors/pressable.js';
83
- export { foldImagePayload, IMAGE_TAG, registerImageBehavior, } from './behaviors/image.js';
84
- export { foldInputAccessoryViewPayload, INPUT_ACCESSORY_VIEW_TAG, registerInputAccessoryViewBehavior, } from './behaviors/input-accessory-view.js';
80
+ export { registerTouchableOpacityBehavior, TOUCHABLE_OPACITY_TAG, } from './behaviors/touchable-opacity.js';
81
+ export { registerTouchableHighlightBehavior, TOUCHABLE_HIGHLIGHT_TAG, } from './behaviors/touchable-highlight.js';
82
+ export { registerButtonBehavior, BUTTON_TAG } from './behaviors/button.js';
83
+ // Registered by ALL FIVE adapters since 2026-09-09, in the same commit that deleted the five
84
+ // wrappers — which is what makes it safe. While those wrappers still rendered a `Pressable` around
85
+ // a feedback `View`, registering would have put a second responder on every existing
86
+ // TouchableNativeFeedback. The ActivityIndicator and ScrollView blocks below are still in the
87
+ // withheld state this one just left.
88
+ export { registerTouchableNativeFeedbackBehavior, TOUCHABLE_NATIVE_FEEDBACK_TAG, } from './behaviors/touchable-native-feedback.js';
89
+ // The second tag that commits no node, and registered by all five adapters in the same commit that
90
+ // deleted their five wrappers — while those wrappers still rendered a `Pressable` around the app's
91
+ // children, registering would have put a second responder on every TouchableWithoutFeedback.
92
+ export { registerTouchableWithoutFeedbackBehavior, TOUCHABLE_WITHOUT_FEEDBACK_TAG, } from './behaviors/touchable-without-feedback.js';
93
+ // Registered by all five adapters since 2026-09-09, in the same commit that deleted the five
94
+ // wrappers — the tag builds its own spinner, so a surviving wrapper would have painted a second one.
95
+ // The prop type rides along: there is no `view/render-activity-indicator.ts` any more, because the
96
+ // only render this primitive had was the two nodes the behavior now builds.
97
+ export { ACTIVITY_INDICATOR_SPINNER_TAG, ACTIVITY_INDICATOR_TAG, registerActivityIndicatorBehavior, } from './behaviors/activity-indicator';
98
+ export { IMAGE_TAG, registerImageBehavior } from './behaviors/image.js';
99
+ // Registered by all five adapters since 2026-09-09, in the same commit that deleted the five
100
+ // wrappers — the tag builds the background image itself, so a surviving wrapper would have
101
+ // committed a second one under it.
102
+ export { IMAGE_BACKGROUND_TAG, registerImageBackgroundBehavior, } from './behaviors/image-background.js';
103
+ export { INPUT_ACCESSORY_VIEW_TAG, registerInputAccessoryViewBehavior, } from './behaviors/input-accessory-view.js';
85
104
  export { registerTextInputBehavior, buildTextInputHandle, TEXT_INPUT_TAG, TEXT_INPUT_MULTILINE_TAG, } from './behaviors/text-input.js';
86
105
  export { registerSwitchBehavior, SWITCH_TAG } from './behaviors/switch.js';
106
+ export { registerRefreshControlBehavior, REFRESH_CONTROL_TAG, } from './behaviors/refresh-control.js';
107
+ // Exported as a FUNCTION, not as a side-effect module, which is what keeps it safe on a barrel:
108
+ // Metro's inlineRequires only defers a re-export until its binding is named as a VALUE, and a
109
+ // caller of `registerScrollViewBehavior()` names it. The shape CLAUDE.md forbids is a module whose
110
+ // evaluation alone registers.
111
+ //
112
+ // EVERY ADAPTER CALLS IT since 2026-09-11, through `@symbiote-native/components/register`. It was
113
+ // withheld while the wrappers built their own content node — registering then would have
114
+ // double-nested every ScrollView. They are gone; the engine is the single owner.
115
+ export { HORIZONTAL_SCROLL_VIEW_TAG, registerScrollViewBehavior, SCROLL_VIEW_TAG, STICKY_HEADER_TAG, } from './behaviors/scroll-view';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,55 @@
1
+ // Side-effect ONLY. Exports nothing, and must never be re-exported from any barrel.
2
+ //
3
+ // The engine's host-behavior registry is what makes a bare `<pressable>` / `<text-input>` /
4
+ // `<switch>` carry its machine; an unregistered tag commits inert. Every adapter needs the same
5
+ // fourteen calls, so they live here once — an adapter's own `src/register.ts` is a single bare
6
+ // `import '@symbiote-native/components/register';`.
7
+ //
8
+ // WHY A SIDE-EFFECT MODULE AND NOT A BARREL EXPORT. Metro turns on `inlineRequires` for PRODUCTION
9
+ // only: it moves a `require` down to the first place its binding is used as a VALUE, and
10
+ // `export { X } from './x'` compiles to a lazy getter. A registration reached only through a
11
+ // re-export therefore never evaluates in a Release build — invisible to tsc, to vitest, and to
12
+ // grepping the bundle. A bare `import` placed NEXT TO a re-export of the same specifier does not
13
+ // help either: Babel merges the two into one dependency and the merged dependency stays lazy.
14
+ // `register.test.ts` is what stops a later tidy-up from turning this back into an export.
15
+ import { registerActivityIndicatorBehavior } from './behaviors/activity-indicator';
16
+ import { registerButtonBehavior } from './behaviors/button.js';
17
+ import { registerImageBackgroundBehavior } from './behaviors/image-background.js';
18
+ import { registerImageBehavior } from './behaviors/image.js';
19
+ import { registerInputAccessoryViewBehavior } from './behaviors/input-accessory-view.js';
20
+ import { registerPressableBehavior } from './behaviors/pressable.js';
21
+ import { registerRefreshControlBehavior } from './behaviors/refresh-control.js';
22
+ import { registerScrollViewBehavior } from './behaviors/scroll-view';
23
+ import { registerSwitchBehavior } from './behaviors/switch.js';
24
+ import { registerTextInputBehavior } from './behaviors/text-input.js';
25
+ import { registerTouchableHighlightBehavior } from './behaviors/touchable-highlight.js';
26
+ import { registerTouchableNativeFeedbackBehavior } from './behaviors/touchable-native-feedback.js';
27
+ import { registerTouchableOpacityBehavior } from './behaviors/touchable-opacity.js';
28
+ import { registerTouchableWithoutFeedbackBehavior } from './behaviors/touchable-without-feedback.js';
29
+ registerPressableBehavior();
30
+ registerTouchableNativeFeedbackBehavior();
31
+ registerTouchableWithoutFeedbackBehavior();
32
+ // Their own tags rather than `pressable`: one node holds exactly one press machine, and each of
33
+ // these is a pressable PLUS a second machine — the opacity fade, the underlay show/hide.
34
+ registerTouchableOpacityBehavior();
35
+ registerTouchableHighlightBehavior();
36
+ // The whole subtree is the behavior's — RN's Button takes no children and builds
37
+ // touchable > view > text > raw text itself (Button.js:363-388).
38
+ registerButtonBehavior();
39
+ // Two nodes, both the behavior's: RN wraps its native spinner in a centering View
40
+ // (ActivityIndicator.js:112), so the tag is that View and `buildStructure` builds the spinner.
41
+ registerActivityIndicatorBehavior();
42
+ registerTextInputBehavior();
43
+ registerSwitchBehavior();
44
+ registerRefreshControlBehavior();
45
+ // Fold-only, both of them: no node of their own, just the prop fold the wrapper used to run.
46
+ registerImageBehavior();
47
+ registerInputAccessoryViewBehavior();
48
+ registerImageBackgroundBehavior();
49
+ // The ENGINE is the single owner of a ScrollView's content node: `buildStructure` builds the
50
+ // `RCTScrollContentView`, `slotProps` carries `contentContainerStyle` onto it, the claim on a
51
+ // `refresh-control` child places it per platform, and the sticky seam is
52
+ // `behaviors/scroll-view/sticky.ts`. EXACTLY ONE THING MAY BUILD THAT NODE — a second owner is a
53
+ // silent `RCTScrollContentView` inside the first, which each adapter's
54
+ // `scroll-view-content-owner` test is what guards.
55
+ registerScrollViewBehavior();
@@ -2,15 +2,9 @@
2
2
  // commits, from the props it was created with.
3
3
  //
4
4
  // Why this has to exist at runtime at all. `intrinsicWhen` lets one primitive map to two native
5
- // views (`TextInput` -> `RCTSinglelineTextInputView` / `RCTMultilineTextInputView`), and until now
6
- // only the lowering transforms read it — so the choice was made at COMPILE time, from the source
7
- // text. That is why `dynamicIntrinsicChoice` had to be a refusal category: a transform seeing
8
- // `multiline={isLong}` cannot know the value, so it refused to lower the element at all.
9
- //
10
- // A public primitive TAG has no transform in front of it on three of the five adapters, so the
11
- // choice has to be made where the value is actually known — here, at element creation. That is a
12
- // capability the compiler never had, and it is why the refusal category disappears WITH the
13
- // transform rather than migrating into it.
5
+ // views (`TextInput` -> `RCTSinglelineTextInputView` / `RCTMultilineTextInputView`), and the
6
+ // deciding prop can be a RUNTIME value: `multiline={isLong}` is knowable only once the element is
7
+ // created. So the choice is made here, where the value is.
14
8
  //
15
9
  // WHAT THIS DELIBERATELY DOES NOT DO: react to the prop CHANGING later. A node's native view is
16
10
  // fixed at `createNode` and no prop write moves a node between view types, so flipping `multiline`
@@ -1,4 +1,4 @@
1
- import { isSymbioteEvent, type AnimatedValue, type IStyleProp, type ISymbioteEvent, type ISymbioteNode, type IViewStyle } from '@symbiote-native/engine';
1
+ import { isSymbioteEvent, type AnimatedValue, type ISymbioteEvent, type ISymbioteNode } from '@symbiote-native/engine';
2
2
  type IScrollHandler = (event: ISymbioteEvent) => void;
3
3
  export interface IScrollViewHandle {
4
4
  scrollTo(options?: {
@@ -12,10 +12,6 @@ export interface IScrollViewHandle {
12
12
  flashScrollIndicators(): void;
13
13
  getScrollNode(): ISymbioteNode | null;
14
14
  }
15
- export declare function splitLayoutProps(style: IStyleProp<IViewStyle> | undefined): {
16
- outer: Record<string, unknown>;
17
- inner: Record<string, unknown>;
18
- };
19
15
  export { isSymbioteEvent };
20
16
  export declare function forwardScrollEvent(handler: IScrollHandler, args: readonly unknown[]): void;
21
17
  export declare function buildScrollViewHandle(getNode: () => ISymbioteNode | null): IScrollViewHandle;
@@ -3,61 +3,18 @@
3
3
  // the Android RefreshControl wrap, the scroll-event guard/forwarder, and the native sticky
4
4
  // scroll-attach are all platform- and framework-invariant, so they live here. The adapter
5
5
  // supplies the lifecycle (the node getter, the effect) and re-exports these.
6
- import { attachNativeEvent, dispatchViewCommand, dlog, flattenStyle, isSymbioteEvent, } from '@symbiote-native/engine';
7
- // RN's splitLayoutProps key partition (StyleSheet/splitLayoutProps.js): the LAYOUT keys
8
- // that belong on the OUTER box when a layout-affecting wrapper sits between the laid-out
9
- // frame and the visual content. Everything NOT in this set (background*, padding*, border*,
10
- // opacity, overflow, …) is VISUAL and stays on the inner view. Replicated exactly from RN's
11
- // switch cases so the Android RefreshControl wrap routes style the way RN does.
12
- const LAYOUT_KEYS = new Set([
13
- 'margin',
14
- 'marginHorizontal',
15
- 'marginVertical',
16
- 'marginBottom',
17
- 'marginTop',
18
- 'marginLeft',
19
- 'marginRight',
20
- 'flex',
21
- 'flexGrow',
22
- 'flexShrink',
23
- 'flexBasis',
24
- 'alignSelf',
25
- 'height',
26
- 'minHeight',
27
- 'maxHeight',
28
- 'width',
29
- 'minWidth',
30
- 'maxWidth',
31
- 'position',
32
- 'left',
33
- 'right',
34
- 'bottom',
35
- 'top',
36
- 'transform',
37
- 'transformOrigin',
38
- 'rowGap',
39
- 'columnGap',
40
- 'gap',
41
- ]);
42
- // Split a flattened style into the LAYOUT props that drive the outer wrapper's frame and the
43
- // VISUAL props that paint the inner content, RN's splitLayoutProps. The Android build uses
44
- // this when a RefreshControl wraps the scroll view: layout (margin/flex/size/position/…) goes
45
- // on the AndroidSwipeRefreshLayout wrapper, visual (background/padding/border/…) stays on the
46
- // inner scroll view, instead of dumping the whole style on the wrapper and hardcoding flex:1.
47
- export function splitLayoutProps(style) {
48
- const outer = {};
49
- const inner = {};
50
- // Reads keys off the style, so flatten the StyleProp (array/nested) to one object first.
51
- const flat = flattenStyle(style);
52
- for (const key of Object.keys(flat)) {
53
- const value = Reflect.get(flat, key);
54
- if (LAYOUT_KEYS.has(key))
55
- outer[key] = value;
56
- else
57
- inner[key] = value;
58
- }
59
- return { outer, inner };
60
- }
6
+ import { attachNativeEvent, dlog, flattenStyle, isSymbioteEvent, } from '@symbiote-native/engine';
7
+ // THE ANDROID WRAP SPLIT LEFT THIS FILE ON 2026-09-18, and with it the last copy of RN's
8
+ // `splitLayoutProps` key partition. Both halves are `SymbioteFabricProps.cpp` now
9
+ // (`splitScrollViewStyle`, `foldRefreshWrapperProps`), reached off the tag and the tree rather than
10
+ // from a `payloadFold` per node — see `behaviors/scroll-view/index.android.ts` for what made the
11
+ // wrapper's half possible at all.
12
+ //
13
+ // Nothing replaced them here on purpose: a JS copy kept for a caller that no longer exists is the
14
+ // mirror shape this migration keeps deleting, and the twenty-eight layout keys are exactly the kind
15
+ // of list that drifts in silence. `core/engine/cpp/tests/js/scroll-view-wrap-payload.itest.ts`
16
+ // asserts the split on the payload a commit actually sent, which is the only place it can now be
17
+ // wrong.
61
18
  // Re-exported so the package barrel (index.ts) can still export this guard to
62
19
  // '@symbiote-native/components' callers, now that it lives in the engine, next to ISymbioteEvent.
63
20
  export { isSymbioteEvent };
@@ -69,42 +26,23 @@ export function forwardScrollEvent(handler, args) {
69
26
  if (isSymbioteEvent(first))
70
27
  handler(first);
71
28
  }
72
- // The imperative handle is identical across platforms: every method dispatches a view
73
- // command on the SAME scroll-view node; only the surrounding element assembly diverges
74
- // (iOS sibling RefreshControl vs Android wrap). So it is built once here and both platform
75
- // files back it with their scroll node getter. Commands and arg order mirror RN's
76
- // ScrollViewCommands: scrollTo [x, y, animated], scrollToEnd [animated], flashScrollIndicators [].
29
+ // The imperative handle is identical across platforms — only the surrounding element assembly
30
+ // diverges (iOS sibling RefreshControl vs Android wrap) — so it is built once here and both
31
+ // platform files back it with their scroll node getter.
32
+ //
33
+ // It DELEGATES to the node's own methods rather than dispatching commands itself. The commands and
34
+ // their defaults live on `ISymbioteNode` because a `<scroll-view>` hands the app its engine node
35
+ // directly, with no wrapper to build a handle: two implementations would let `scrollTo()` with no
36
+ // argument mean one thing through a ref and another through a tag, and nothing would report it.
77
37
  //
78
38
  // `getNode` is a LAZY getter (React `() => ref.current`, Vue `() => nodeRef.value`), read on
79
39
  // every call, NOT the node captured once. The node is null at mount and only set after the
80
40
  // element commits, so an eager capture would freeze `null` and every command would no-op.
81
41
  export function buildScrollViewHandle(getNode) {
82
42
  return {
83
- scrollTo: (options) => {
84
- const node = getNode();
85
- if (node === null)
86
- return;
87
- const x = options?.x ?? 0;
88
- const y = options?.y ?? 0;
89
- const animated = options?.animated ?? true;
90
- dlog(`ScrollView.scrollTo x=${x} y=${y} animated=${animated}`);
91
- dispatchViewCommand(node, 'scrollTo', [x, y, animated]);
92
- },
93
- scrollToEnd: (options) => {
94
- const node = getNode();
95
- if (node === null)
96
- return;
97
- const animated = options?.animated ?? true;
98
- dlog(`ScrollView.scrollToEnd animated=${animated}`);
99
- dispatchViewCommand(node, 'scrollToEnd', [animated]);
100
- },
101
- flashScrollIndicators: () => {
102
- const node = getNode();
103
- if (node === null)
104
- return;
105
- dlog('ScrollView.flashScrollIndicators');
106
- dispatchViewCommand(node, 'flashScrollIndicators', []);
107
- },
43
+ scrollTo: (options) => getNode()?.scrollTo(options),
44
+ scrollToEnd: (options) => getNode()?.scrollToEnd(options),
45
+ flashScrollIndicators: () => getNode()?.flashScrollIndicators(),
108
46
  getScrollNode: () => getNode(),
109
47
  };
110
48
  }
@@ -1,11 +1,11 @@
1
- import type { IViewableItemsChangedInfo, IViewToken } from './virtualized-list';
1
+ import { type IViewableItemsChangedInfo, type IViewToken } from './virtualized-list';
2
2
  export declare const SINGLE_COLUMN = 1;
3
3
  export interface IRow<ItemT> {
4
4
  items: ItemT[];
5
5
  startIndex: number;
6
6
  }
7
7
  export declare function chunkIntoRows<ItemT>(data: readonly ItemT[], columns: number): IRow<ItemT>[];
8
- export declare function rowKeyExtractor<ItemT>(row: IRow<ItemT>): string;
8
+ export declare function rowKeyExtractor<ItemT>(row: IRow<ItemT>, keyExtractor?: (item: ItemT, index: number) => string): string;
9
9
  export declare function expandRowToken<ItemT>(token: IViewToken<IRow<ItemT>>, keyExtractor?: (item: ItemT, index: number) => string): IViewToken<ItemT>[];
10
10
  export declare function expandRowViewability<ItemT>(info: IViewableItemsChangedInfo<IRow<ItemT>>, keyExtractor?: (item: ItemT, index: number) => string): IViewableItemsChangedInfo<ItemT>;
11
11
  export declare function lastItemOfRow<ItemT>(row: IRow<ItemT> | undefined): ItemT | undefined;
@@ -4,6 +4,7 @@
4
4
  // back to per-item tokens, and the row separator unwraps to the flanking items. All of
5
5
  // this is pure transform — every adapter (React, Vue) reuses it; the adapter supplies
6
6
  // only the element creation (createElement / h) for a row and the ref wiring.
7
+ import { defaultKeyExtractor, } from './virtualized-list.js';
7
8
  export const SINGLE_COLUMN = 1;
8
9
  export function chunkIntoRows(data, columns) {
9
10
  const rows = [];
@@ -12,8 +13,15 @@ export function chunkIntoRows(data, columns) {
12
13
  }
13
14
  return rows;
14
15
  }
15
- export function rowKeyExtractor(row) {
16
- return `row-${row.startIndex}`;
16
+ // RN's own multi-column row key (`FlatList.js`'s `_keyExtractor`): each item's own key, joined
17
+ // with `:`, never a synthetic position string — an app's `keyExtractor` (or the object's own
18
+ // `key`/`id`) is what keeps a row's identity stable across inserts/removes, and a `row-${index}`
19
+ // key defeats that as surely as no key at all.
20
+ export function rowKeyExtractor(row, keyExtractor) {
21
+ const resolve = keyExtractor ?? defaultKeyExtractor;
22
+ return row.items
23
+ .map((item, column) => resolve(item, row.startIndex + column))
24
+ .join(':');
17
25
  }
18
26
  // Expand a row's viewable token to one token per item in that row, all sharing the row's
19
27
  // isViewable flag, so the caller sees item-level visibility rather than row-level.